Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2007 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 android.util;
     18 
     19 import java.io.PrintWriter;
     20 import java.lang.reflect.Field;
     21 import java.lang.reflect.InvocationTargetException;
     22 import java.lang.reflect.Method;
     23 import java.lang.reflect.Modifier;
     24 import java.util.Locale;
     25 
     26 /**
     27  * <p>Various utilities for debugging and logging.</p>
     28  */
     29 public class DebugUtils {
     30     /** @hide */ public DebugUtils() {}
     31 
     32     /**
     33      * <p>Filters objects against the <code>ANDROID_OBJECT_FILTER</code>
     34      * environment variable. This environment variable can filter objects
     35      * based on their class name and attribute values.</p>
     36      *
     37      * <p>Here is the syntax for <code>ANDROID_OBJECT_FILTER</code>:</p>
     38      *
     39      * <p><code>ClassName@attribute1=value1@attribute2=value2...</code></p>
     40      *
     41      * <p>Examples:</p>
     42      * <ul>
     43      * <li>Select TextView instances: <code>TextView</code></li>
     44      * <li>Select TextView instances of text "Loading" and bottom offset of 22:
     45      * <code>TextView@text=Loading.*@bottom=22</code></li>
     46      * </ul>
     47      *
     48      * <p>The class name and the values are regular expressions.</p>
     49      *
     50      * <p>This class is useful for debugging and logging purpose:</p>
     51      * <pre>
     52      * if (DEBUG) {
     53      *   if (DebugUtils.isObjectSelected(childView) && LOGV_ENABLED) {
     54      *     Log.v(TAG, "Object " + childView + " logged!");
     55      *   }
     56      * }
     57      * </pre>
     58      *
     59      * <p><strong>NOTE</strong>: This method is very expensive as it relies
     60      * heavily on regular expressions and reflection. Calls to this method
     61      * should always be stripped out of the release binaries and avoided
     62      * as much as possible in debug mode.</p>
     63      *
     64      * @param object any object to match against the ANDROID_OBJECT_FILTER
     65      *        environement variable
     66      * @return true if object is selected by the ANDROID_OBJECT_FILTER
     67      *         environment variable, false otherwise
     68      */
     69     public static boolean isObjectSelected(Object object) {
     70         boolean match = false;
     71         String s = System.getenv("ANDROID_OBJECT_FILTER");
     72         if (s != null && s.length() > 0) {
     73             String[] selectors = s.split("@");
     74             // first selector == class name
     75             if (object.getClass().getSimpleName().matches(selectors[0])) {
     76                 // check potential attributes
     77                 for (int i = 1; i < selectors.length; i++) {
     78                     String[] pair = selectors[i].split("=");
     79                     Class<?> klass = object.getClass();
     80                     try {
     81                         Method declaredMethod = null;
     82                         Class<?> parent = klass;
     83                         do {
     84                             declaredMethod = parent.getDeclaredMethod("get" +
     85                                     pair[0].substring(0, 1).toUpperCase(Locale.ROOT) +
     86                                     pair[0].substring(1),
     87                                     (Class[]) null);
     88                         } while ((parent = klass.getSuperclass()) != null &&
     89                                 declaredMethod == null);
     90 
     91                         if (declaredMethod != null) {
     92                             Object value = declaredMethod
     93                                     .invoke(object, (Object[])null);
     94                             match |= (value != null ?
     95                                     value.toString() : "null").matches(pair[1]);
     96                         }
     97                     } catch (NoSuchMethodException e) {
     98                         e.printStackTrace();
     99                     } catch (IllegalAccessException e) {
    100                         e.printStackTrace();
    101                     } catch (InvocationTargetException e) {
    102                         e.printStackTrace();
    103                     }
    104                 }
    105             }
    106         }
    107         return match;
    108     }
    109 
    110     /** @hide */
    111     public static void buildShortClassTag(Object cls, StringBuilder out) {
    112         if (cls == null) {
    113             out.append("null");
    114         } else {
    115             String simpleName = cls.getClass().getSimpleName();
    116             if (simpleName == null || simpleName.isEmpty()) {
    117                 simpleName = cls.getClass().getName();
    118                 int end = simpleName.lastIndexOf('.');
    119                 if (end > 0) {
    120                     simpleName = simpleName.substring(end+1);
    121                 }
    122             }
    123             out.append(simpleName);
    124             out.append('{');
    125             out.append(Integer.toHexString(System.identityHashCode(cls)));
    126         }
    127     }
    128 
    129     /** @hide */
    130     public static void printSizeValue(PrintWriter pw, long number) {
    131         float result = number;
    132         String suffix = "";
    133         if (result > 900) {
    134             suffix = "KB";
    135             result = result / 1024;
    136         }
    137         if (result > 900) {
    138             suffix = "MB";
    139             result = result / 1024;
    140         }
    141         if (result > 900) {
    142             suffix = "GB";
    143             result = result / 1024;
    144         }
    145         if (result > 900) {
    146             suffix = "TB";
    147             result = result / 1024;
    148         }
    149         if (result > 900) {
    150             suffix = "PB";
    151             result = result / 1024;
    152         }
    153         String value;
    154         if (result < 1) {
    155             value = String.format("%.2f", result);
    156         } else if (result < 10) {
    157             value = String.format("%.1f", result);
    158         } else if (result < 100) {
    159             value = String.format("%.0f", result);
    160         } else {
    161             value = String.format("%.0f", result);
    162         }
    163         pw.print(value);
    164         pw.print(suffix);
    165     }
    166 
    167     /** @hide */
    168     public static String sizeValueToString(long number, StringBuilder outBuilder) {
    169         if (outBuilder == null) {
    170             outBuilder = new StringBuilder(32);
    171         }
    172         float result = number;
    173         String suffix = "";
    174         if (result > 900) {
    175             suffix = "KB";
    176             result = result / 1024;
    177         }
    178         if (result > 900) {
    179             suffix = "MB";
    180             result = result / 1024;
    181         }
    182         if (result > 900) {
    183             suffix = "GB";
    184             result = result / 1024;
    185         }
    186         if (result > 900) {
    187             suffix = "TB";
    188             result = result / 1024;
    189         }
    190         if (result > 900) {
    191             suffix = "PB";
    192             result = result / 1024;
    193         }
    194         String value;
    195         if (result < 1) {
    196             value = String.format("%.2f", result);
    197         } else if (result < 10) {
    198             value = String.format("%.1f", result);
    199         } else if (result < 100) {
    200             value = String.format("%.0f", result);
    201         } else {
    202             value = String.format("%.0f", result);
    203         }
    204         outBuilder.append(value);
    205         outBuilder.append(suffix);
    206         return outBuilder.toString();
    207     }
    208 
    209     /**
    210      * Use prefixed constants (static final values) on given class to turn value
    211      * into human-readable string.
    212      *
    213      * @hide
    214      */
    215     public static String valueToString(Class<?> clazz, String prefix, int value) {
    216         for (Field field : clazz.getDeclaredFields()) {
    217             final int modifiers = field.getModifiers();
    218             if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
    219                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
    220                 try {
    221                     if (value == field.getInt(null)) {
    222                         return field.getName().substring(prefix.length());
    223                     }
    224                 } catch (IllegalAccessException ignored) {
    225                 }
    226             }
    227         }
    228         return Integer.toString(value);
    229     }
    230 
    231     /**
    232      * Use prefixed constants (static final values) on given class to turn flags
    233      * into human-readable string.
    234      *
    235      * @hide
    236      */
    237     public static String flagsToString(Class<?> clazz, String prefix, int flags) {
    238         final StringBuilder res = new StringBuilder();
    239 
    240         for (Field field : clazz.getDeclaredFields()) {
    241             final int modifiers = field.getModifiers();
    242             if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
    243                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
    244                 try {
    245                     final int value = field.getInt(null);
    246                     if ((flags & value) != 0) {
    247                         flags &= ~value;
    248                         res.append(field.getName().substring(prefix.length())).append('|');
    249                     }
    250                 } catch (IllegalAccessException ignored) {
    251                 }
    252             }
    253         }
    254         if (flags != 0 || res.length() == 0) {
    255             res.append(Integer.toHexString(flags));
    256         } else {
    257             res.deleteCharAt(res.length() - 1);
    258         }
    259         return res.toString();
    260     }
    261 }
    262