Home | History | Annotate | Download | only in common
      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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    247     private SoftPreconditions() {}
    248 }
    249