1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.util.proxy;
17
18import java.lang.reflect.Method;
19import java.io.Serializable;
20
21/**
22 * Runtime support routines that the classes generated by ProxyFactory use.
23 *
24 * @see ProxyFactory
25 */
26public class RuntimeSupport {
27    /**
28     * A method handler that only executes a method.
29     */
30    public static MethodHandler default_interceptor = new DefaultMethodHandler();
31
32    static class DefaultMethodHandler implements MethodHandler, Serializable {
33        public Object invoke(Object self, Method m,
34                             Method proceed, Object[] args)
35            throws Exception
36        {
37            return proceed.invoke(self, args);
38        }
39    };
40
41    /**
42     * Finds two methods specified by the parameters and stores them
43     * into the given array.
44     *
45     * @throws RuntimeException     if the methods are not found.
46     * @see javassist.util.proxy.ProxyFactory
47     */
48    public static void find2Methods(Object self, String superMethod,
49                                    String thisMethod, int index,
50                                    String desc, java.lang.reflect.Method[] methods)
51    {
52        synchronized (methods) {
53            if (methods[index] == null) {
54                methods[index + 1] = thisMethod == null ? null
55                                     : findMethod(self, thisMethod, desc);
56                methods[index] = findSuperMethod(self, superMethod, desc);
57            }
58        }
59    }
60
61    /**
62     * Finds a method with the given name and descriptor.
63     * It searches only the class of self.
64     *
65     * @throws RuntimeException     if the method is not found.
66     */
67    public static Method findMethod(Object self, String name, String desc) {
68        Method m = findMethod2(self.getClass(), name, desc);
69        if (m == null)
70            error(self, name, desc);
71
72        return m;
73    }
74
75    /**
76     * Finds a method that has the given name and descriptor and is declared
77     * in the super class.
78     *
79     * @throws RuntimeException     if the method is not found.
80     */
81    public static Method findSuperMethod(Object self, String name, String desc) {
82        Class clazz = self.getClass();
83        Method m = findSuperMethod2(clazz.getSuperclass(), name, desc);
84        if (m == null)
85            m = searchInterfaces(clazz, name, desc);
86
87        if (m == null)
88            error(self, name, desc);
89
90        return m;
91    }
92
93    private static void error(Object self, String name, String desc) {
94        throw new RuntimeException("not found " + name + ":" + desc
95                + " in " + self.getClass().getName());
96    }
97
98    private static Method findSuperMethod2(Class clazz, String name, String desc) {
99        Method m = findMethod2(clazz, name, desc);
100        if (m != null)
101            return m;
102
103        Class superClass = clazz.getSuperclass();
104        if (superClass != null) {
105            m = findSuperMethod2(superClass, name, desc);
106            if (m != null)
107                return m;
108        }
109
110        return searchInterfaces(clazz, name, desc);
111    }
112
113    private static Method searchInterfaces(Class clazz, String name, String desc) {
114        Method m = null;
115        Class[] interfaces = clazz.getInterfaces();
116        for (int i = 0; i < interfaces.length; i++) {
117            m = findSuperMethod2(interfaces[i], name, desc);
118            if (m != null)
119                return m;
120        }
121
122        return m;
123    }
124
125    private static Method findMethod2(Class clazz, String name, String desc) {
126        Method[] methods = SecurityActions.getDeclaredMethods(clazz);
127        int n = methods.length;
128        for (int i = 0; i < n; i++)
129            if (methods[i].getName().equals(name)
130                && makeDescriptor(methods[i]).equals(desc))
131            return methods[i];
132
133        return null;
134    }
135
136    /**
137     * Makes a descriptor for a given method.
138     */
139    public static String makeDescriptor(Method m) {
140        Class[] params = m.getParameterTypes();
141        return makeDescriptor(params, m.getReturnType());
142    }
143
144    /**
145     * Makes a descriptor for a given method.
146     *
147     * @param params    parameter types.
148     * @param retType   return type.
149     */
150    public static String makeDescriptor(Class[] params, Class retType) {
151        StringBuffer sbuf = new StringBuffer();
152        sbuf.append('(');
153        for (int i = 0; i < params.length; i++)
154            makeDesc(sbuf, params[i]);
155
156        sbuf.append(')');
157        makeDesc(sbuf, retType);
158        return sbuf.toString();
159    }
160
161    private static void makeDesc(StringBuffer sbuf, Class type) {
162        if (type.isArray()) {
163            sbuf.append('[');
164            makeDesc(sbuf, type.getComponentType());
165        }
166        else if (type.isPrimitive()) {
167            if (type == Void.TYPE)
168                sbuf.append('V');
169            else if (type == Integer.TYPE)
170                sbuf.append('I');
171            else if (type == Byte.TYPE)
172                sbuf.append('B');
173            else if (type == Long.TYPE)
174                sbuf.append('J');
175            else if (type == Double.TYPE)
176                sbuf.append('D');
177            else if (type == Float.TYPE)
178                sbuf.append('F');
179            else if (type == Character.TYPE)
180                sbuf.append('C');
181            else if (type == Short.TYPE)
182                sbuf.append('S');
183            else if (type == Boolean.TYPE)
184                sbuf.append('Z');
185            else
186                throw new RuntimeException("bad type: " + type.getName());
187        }
188        else
189            sbuf.append('L').append(type.getName().replace('.', '/'))
190                .append(';');
191    }
192
193    /**
194     * Converts a proxy object to an object that is writable to an
195     * object stream.  This method is called by <code>writeReplace()</code>
196     * in a proxy class.
197     *
198     * @since 3.4
199     */
200    public static SerializedProxy makeSerializedProxy(Object proxy)
201        throws java.io.InvalidClassException
202    {
203        Class clazz = proxy.getClass();
204
205        MethodHandler methodHandler = null;
206        if (proxy instanceof ProxyObject)
207            methodHandler = ((ProxyObject)proxy).getHandler();
208
209        return new SerializedProxy(clazz, ProxyFactory.getFilterSignature(clazz), methodHandler);
210    }
211}
212