1/*
2 * Copyright (C) 2008 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 com.android.tools.layoutlib.create;
18
19import java.util.HashMap;
20
21/**
22 * Allows stub methods from LayoutLib to be overriden at runtime.
23 * <p/>
24 * Implementation note: all types required by this class(inner/outer classes & interfaces)
25 * must be referenced by the injectClass argument to {@link AsmGenerator} in Main.java;
26 * Otherwise they won't be accessible in layoutlib.jar at runtime.
27 */
28public final class OverrideMethod {
29
30    /** Map of method overridden. */
31    private static HashMap<String, MethodListener> sMethods = new HashMap<String, MethodListener>();
32    /** Default listener for all method not listed in sMethods. Nothing if null. */
33    private static MethodListener sDefaultListener = null;
34
35    /**
36     * Sets the default listener for all methods not specifically handled.
37     * Null means to do nothing.
38     */
39    @SuppressWarnings("UnusedDeclaration") // Used by Bridge by reflection for debug purposes.
40    public static void setDefaultListener(MethodListener listener) {
41        sDefaultListener = listener;
42    }
43
44    /**
45     * Defines or reset a listener for the given method signature.
46     *
47     * @param signature The signature of the method being invoked, composed of the
48     *                  binary class name followed by the method descriptor (aka argument
49     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V"
50     * @param listener The new listener. Removes it if null.
51     */
52    public static void setMethodListener(String signature, MethodListener listener) {
53        if (listener == null) {
54            sMethods.remove(signature);
55        } else {
56            sMethods.put(signature, listener);
57        }
58    }
59
60    /**
61     * Invokes the specific listener for the given signature or the default one if defined.
62     * <p/>
63     * This version invokes the method listener for the void return type.
64     * <p/>
65     * Note: this is not intended to be used by the LayoutLib Bridge. It is intended to be called
66     * by the stubbed methods generated by the LayoutLib_create tool.
67     *
68     * @param signature The signature of the method being invoked, composed of the
69     *                  binary class name followed by the method descriptor (aka argument
70     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
71     * @param isNative True if the method was a native method.
72     * @param caller The calling object. Null for static methods, "this" for instance methods.
73     */
74    public static void invokeV(String signature, boolean isNative, Object caller) {
75        MethodListener i = sMethods.get(signature);
76        if (i != null) {
77            i.onInvokeV(signature, isNative, caller);
78        } else if (sDefaultListener != null) {
79            sDefaultListener.onInvokeV(signature, isNative, caller);
80        }
81    }
82
83    /**
84     * Invokes the specific listener for the int return type.
85     * @see #invokeV(String, boolean, Object)
86     */
87    public static int invokeI(String signature, boolean isNative, Object caller) {
88        MethodListener i = sMethods.get(signature);
89        if (i != null) {
90            return i.onInvokeI(signature, isNative, caller);
91        } else if (sDefaultListener != null) {
92            return sDefaultListener.onInvokeI(signature, isNative, caller);
93        }
94        return 0;
95    }
96
97    /**
98     * Invokes the specific listener for the long return type.
99     * @see #invokeV(String, boolean, Object)
100     */
101    public static long invokeL(String signature, boolean isNative, Object caller) {
102        MethodListener i = sMethods.get(signature);
103        if (i != null) {
104            return i.onInvokeL(signature, isNative, caller);
105        } else if (sDefaultListener != null) {
106            return sDefaultListener.onInvokeL(signature, isNative, caller);
107        }
108        return 0;
109    }
110
111    /**
112     * Invokes the specific listener for the float return type.
113     * @see #invokeV(String, boolean, Object)
114     */
115    public static float invokeF(String signature, boolean isNative, Object caller) {
116        MethodListener i = sMethods.get(signature);
117        if (i != null) {
118            return i.onInvokeF(signature, isNative, caller);
119        } else if (sDefaultListener != null) {
120            return sDefaultListener.onInvokeF(signature, isNative, caller);
121        }
122        return 0;
123    }
124
125    /**
126     * Invokes the specific listener for the double return type.
127     * @see #invokeV(String, boolean, Object)
128     */
129    public static double invokeD(String signature, boolean isNative, Object caller) {
130        MethodListener i = sMethods.get(signature);
131        if (i != null) {
132            return i.onInvokeD(signature, isNative, caller);
133        } else if (sDefaultListener != null) {
134            return sDefaultListener.onInvokeD(signature, isNative, caller);
135        }
136        return 0;
137    }
138
139    /**
140     * Invokes the specific listener for the object return type.
141     * @see #invokeV(String, boolean, Object)
142     */
143    public static Object invokeA(String signature, boolean isNative, Object caller) {
144        MethodListener i = sMethods.get(signature);
145        if (i != null) {
146            return i.onInvokeA(signature, isNative, caller);
147        } else if (sDefaultListener != null) {
148            return sDefaultListener.onInvokeA(signature, isNative, caller);
149        }
150        return null;
151    }
152}
153