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
17package android.util;
18
19import java.lang.reflect.Method;
20import java.lang.reflect.InvocationTargetException;
21import java.util.Locale;
22
23/**
24 * <p>Various utilities for debugging and logging.</p>
25 */
26public class DebugUtils {
27    /** @hide */ public DebugUtils() {}
28
29    /**
30     * <p>Filters objects against the <code>ANDROID_OBJECT_FILTER</code>
31     * environment variable. This environment variable can filter objects
32     * based on their class name and attribute values.</p>
33     *
34     * <p>Here is the syntax for <code>ANDROID_OBJECT_FILTER</code>:</p>
35     *
36     * <p><code>ClassName@attribute1=value1@attribute2=value2...</code></p>
37     *
38     * <p>Examples:</p>
39     * <ul>
40     * <li>Select TextView instances: <code>TextView</code></li>
41     * <li>Select TextView instances of text "Loading" and bottom offset of 22:
42     * <code>TextView@text=Loading.*@bottom=22</code></li>
43     * </ul>
44     *
45     * <p>The class name and the values are regular expressions.</p>
46     *
47     * <p>This class is useful for debugging and logging purpose:</p>
48     * <pre>
49     * if (DEBUG) {
50     *   if (DebugUtils.isObjectSelected(childView) && LOGV_ENABLED) {
51     *     Log.v(TAG, "Object " + childView + " logged!");
52     *   }
53     * }
54     * </pre>
55     *
56     * <p><strong>NOTE</strong>: This method is very expensive as it relies
57     * heavily on regular expressions and reflection. Calls to this method
58     * should always be stripped out of the release binaries and avoided
59     * as much as possible in debug mode.</p>
60     *
61     * @param object any object to match against the ANDROID_OBJECT_FILTER
62     *        environement variable
63     * @return true if object is selected by the ANDROID_OBJECT_FILTER
64     *         environment variable, false otherwise
65     */
66    public static boolean isObjectSelected(Object object) {
67        boolean match = false;
68        String s = System.getenv("ANDROID_OBJECT_FILTER");
69        if (s != null && s.length() > 0) {
70            String[] selectors = s.split("@");
71            // first selector == class name
72            if (object.getClass().getSimpleName().matches(selectors[0])) {
73                // check potential attributes
74                for (int i = 1; i < selectors.length; i++) {
75                    String[] pair = selectors[i].split("=");
76                    Class<?> klass = object.getClass();
77                    try {
78                        Method declaredMethod = null;
79                        Class<?> parent = klass;
80                        do {
81                            declaredMethod = parent.getDeclaredMethod("get" +
82                                    pair[0].substring(0, 1).toUpperCase(Locale.ROOT) +
83                                    pair[0].substring(1),
84                                    (Class[]) null);
85                        } while ((parent = klass.getSuperclass()) != null &&
86                                declaredMethod == null);
87
88                        if (declaredMethod != null) {
89                            Object value = declaredMethod
90                                    .invoke(object, (Object[])null);
91                            match |= (value != null ?
92                                    value.toString() : "null").matches(pair[1]);
93                        }
94                    } catch (NoSuchMethodException e) {
95                        e.printStackTrace();
96                    } catch (IllegalAccessException e) {
97                        e.printStackTrace();
98                    } catch (InvocationTargetException e) {
99                        e.printStackTrace();
100                    }
101                }
102            }
103        }
104        return match;
105    }
106
107    /** @hide */
108    public static void buildShortClassTag(Object cls, StringBuilder out) {
109        if (cls == null) {
110            out.append("null");
111        } else {
112            String simpleName = cls.getClass().getSimpleName();
113            if (simpleName == null || simpleName.isEmpty()) {
114                simpleName = cls.getClass().getName();
115                int end = simpleName.lastIndexOf('.');
116                if (end > 0) {
117                    simpleName = simpleName.substring(end+1);
118                }
119            }
120            out.append(simpleName);
121            out.append('{');
122            out.append(Integer.toHexString(System.identityHashCode(cls)));
123        }
124    }
125
126}
127