1 /* 2 * Copyright (C) 2015 The Android Open Source Project 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 17 package com.android.tv.common; 18 19 import android.content.Context; 20 import android.support.annotation.Nullable; 21 import android.text.TextUtils; 22 import android.util.Log; 23 import com.android.tv.common.feature.Feature; 24 import com.android.tv.common.util.CommonUtils; 25 26 /** 27 * Simple static methods to be called at the start of your own methods to verify correct arguments 28 * and state. 29 * 30 * <p>{@code checkXXX} methods throw exceptions when {@link BuildConfig#ENG} is true, and logs a 31 * warning when it is false. 32 * 33 * <p>This is based on com.android.internal.util.Preconditions. 34 */ 35 public final class SoftPreconditions { 36 private static final String TAG = "SoftPreconditions"; 37 38 /** 39 * Throws or logs if an expression involving the parameter of the calling method is not true. 40 * 41 * @param expression a boolean expression 42 * @param tag Used to identify the source of a log message. It usually identifies the class or 43 * activity where the log call occurs. 44 * @param errorMessageTemplate a template for the exception message should the check fail. The 45 * message is formed by replacing each {@code %s} placeholder in the template with an 46 * argument. These are matched by position - the first {@code %s} gets {@code 47 * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message 48 * in square braces. Unmatched placeholders will be left as-is. 49 * @param errorMessageArgs the arguments to be substituted into the message template. Arguments 50 * are converted to strings using {@link String#valueOf(Object)}. 51 * @return the evaluation result of the boolean expression 52 * @throws IllegalArgumentException if {@code expression} is true 53 */ checkArgument( final boolean expression, String tag, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs)54 public static boolean checkArgument( 55 final boolean expression, 56 String tag, 57 @Nullable String errorMessageTemplate, 58 @Nullable Object... errorMessageArgs) { 59 if (!expression) { 60 String msg = format(errorMessageTemplate, errorMessageArgs); 61 warn(tag, "Illegal argument", new IllegalArgumentException(msg), msg); 62 } 63 return expression; 64 } 65 66 /** 67 * Throws or logs if an expression involving the parameter of the calling method is not true. 68 * 69 * @param expression a boolean expression 70 * @return the evaluation result of the boolean expression 71 * @throws IllegalArgumentException if {@code expression} is true 72 */ checkArgument(final boolean expression)73 public static boolean checkArgument(final boolean expression) { 74 checkArgument(expression, null, null); 75 return expression; 76 } 77 78 /** 79 * Throws or logs if an and object is null. 80 * 81 * @param reference an object reference 82 * @param tag Used to identify the source of a log message. It usually identifies the class or 83 * activity where the log call occurs. 84 * @param errorMessageTemplate a template for the exception message should the check fail. The 85 * message is formed by replacing each {@code %s} placeholder in the template with an 86 * argument. These are matched by position - the first {@code %s} gets {@code 87 * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message 88 * in square braces. Unmatched placeholders will be left as-is. 89 * @param errorMessageArgs the arguments to be substituted into the message template. Arguments 90 * are converted to strings using {@link String#valueOf(Object)}. 91 * @return true if the object is null 92 * @throws NullPointerException if {@code reference} is null 93 */ checkNotNull( final T reference, String tag, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs)94 public static <T> T checkNotNull( 95 final T reference, 96 String tag, 97 @Nullable String errorMessageTemplate, 98 @Nullable Object... errorMessageArgs) { 99 if (reference == null) { 100 String msg = format(errorMessageTemplate, errorMessageArgs); 101 warn(tag, "Null Pointer", new NullPointerException(msg), msg); 102 } 103 return reference; 104 } 105 106 /** 107 * Throws or logs if an and object is null. 108 * 109 * @param reference an object reference 110 * @return true if the object is null 111 * @throws NullPointerException if {@code reference} is null 112 */ checkNotNull(final T reference)113 public static <T> T checkNotNull(final T reference) { 114 return checkNotNull(reference, null, null); 115 } 116 117 /** 118 * Throws or logs if an expression involving the state of the calling instance, but not 119 * involving any parameters to the calling method is not true. 120 * 121 * @param expression a boolean expression 122 * @param tag Used to identify the source of a log message. It usually identifies the class or 123 * activity where the log call occurs. 124 * @param errorMessageTemplate a template for the exception message should the check fail. The 125 * message is formed by replacing each {@code %s} placeholder in the template with an 126 * argument. These are matched by position - the first {@code %s} gets {@code 127 * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message 128 * in square braces. Unmatched placeholders will be left as-is. 129 * @param errorMessageArgs the arguments to be substituted into the message template. Arguments 130 * are converted to strings using {@link String#valueOf(Object)}. 131 * @return the evaluation result of the boolean expression 132 * @throws IllegalStateException if {@code expression} is true 133 */ checkState( final boolean expression, String tag, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs)134 public static boolean checkState( 135 final boolean expression, 136 String tag, 137 @Nullable String errorMessageTemplate, 138 @Nullable Object... errorMessageArgs) { 139 if (!expression) { 140 String msg = format(errorMessageTemplate, errorMessageArgs); 141 warn(tag, "Illegal State", new IllegalStateException(msg), msg); 142 } 143 return expression; 144 } 145 146 /** 147 * Throws or logs if an expression involving the state of the calling instance, but not 148 * involving any parameters to the calling method is not true. 149 * 150 * @param expression a boolean expression 151 * @return the evaluation result of the boolean expression 152 * @throws IllegalStateException if {@code expression} is true 153 */ checkState(final boolean expression)154 public static boolean checkState(final boolean expression) { 155 checkState(expression, null, null); 156 return expression; 157 } 158 159 /** 160 * Throws or logs if the Feature is not enabled 161 * 162 * @param context an android context 163 * @param feature the required feature 164 * @param tag used to identify the source of a log message. It usually identifies the class or 165 * activity where the log call occurs 166 * @throws IllegalStateException if {@code feature} is not enabled 167 */ checkFeatureEnabled(Context context, Feature feature, String tag)168 public static void checkFeatureEnabled(Context context, Feature feature, String tag) { 169 checkState(feature.isEnabled(context), tag, feature.toString()); 170 } 171 172 /** 173 * Throws a {@link RuntimeException} if {@link BuildConfig#ENG} is true and not running in a 174 * test, else log a warning. 175 * 176 * @param tag Used to identify the source of a log message. It usually identifies the class or 177 * activity where the log call occurs. 178 * @param e The exception to wrap with a RuntimeException when thrown. 179 * @param msg The message to be logged 180 */ warn(String tag, String prefix, Exception e, String msg)181 public static void warn(String tag, String prefix, Exception e, String msg) 182 throws RuntimeException { 183 if (TextUtils.isEmpty(tag)) { 184 tag = TAG; 185 } 186 String logMessage; 187 if (TextUtils.isEmpty(msg)) { 188 logMessage = prefix; 189 } else if (TextUtils.isEmpty(prefix)) { 190 logMessage = msg; 191 } else { 192 logMessage = prefix + ": " + msg; 193 } 194 195 if (BuildConfig.ENG && !CommonUtils.isRunningInTest()) { 196 throw new RuntimeException(msg, e); 197 } else { 198 Log.w(tag, logMessage, e); 199 } 200 } 201 202 /** 203 * Substitutes each {@code %s} in {@code template} with an argument. These are matched by 204 * position: the first {@code %s} gets {@code args[0]}, etc. If there are more arguments than 205 * placeholders, the unmatched arguments will be appended to the end of the formatted message in 206 * square braces. 207 * 208 * @param template a string containing 0 or more {@code %s} placeholders. null is treated as 209 * "null". 210 * @param args the arguments to be substituted into the message template. Arguments are 211 * converted to strings using {@link String#valueOf(Object)}. Arguments can be null. 212 */ format(@ullable String template, @Nullable Object... args)213 static String format(@Nullable String template, @Nullable Object... args) { 214 template = String.valueOf(template); // null -> "null" 215 216 args = args == null ? new Object[] {"(Object[])null"} : args; 217 218 // start substituting the arguments into the '%s' placeholders 219 StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); 220 int templateStart = 0; 221 int i = 0; 222 while (i < args.length) { 223 int placeholderStart = template.indexOf("%s", templateStart); 224 if (placeholderStart == -1) { 225 break; 226 } 227 builder.append(template, templateStart, placeholderStart); 228 builder.append(args[i++]); 229 templateStart = placeholderStart + 2; 230 } 231 builder.append(template, templateStart, template.length()); 232 233 // if we run out of placeholders, append the extra args in square braces 234 if (i < args.length) { 235 builder.append(" ["); 236 builder.append(args[i++]); 237 while (i < args.length) { 238 builder.append(", "); 239 builder.append(args[i++]); 240 } 241 builder.append(']'); 242 } 243 244 return builder.toString(); 245 } 246 SoftPreconditions()247 private SoftPreconditions() {} 248 } 249