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.BufferedOutputStream;
20import java.io.ByteArrayOutputStream;
21import java.io.DataOutputStream;
22import java.io.File;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.security.ProtectionDomain;
26
27import javassist.CannotCompileException;
28import javassist.bytecode.ClassFile;
29
30/**
31 * A helper class for implementing <code>ProxyFactory</code>.
32 * The users of <code>ProxyFactory</code> do not have to see this class.
33 *
34 * @see ProxyFactory
35 */
36public class FactoryHelper {
37    private static java.lang.reflect.Method defineClass1, defineClass2;
38
39    static {
40        try {
41            Class cl = Class.forName("java.lang.ClassLoader");
42            defineClass1 = SecurityActions.getDeclaredMethod(
43                        cl,
44                        "defineClass",
45                        new Class[] { String.class, byte[].class,
46                                      int.class, int.class });
47
48            defineClass2 = SecurityActions.getDeclaredMethod(
49                        cl,
50                        "defineClass",
51                        new Class[] { String.class, byte[].class,
52                              int.class, int.class, ProtectionDomain.class });
53        }
54        catch (Exception e) {
55            throw new RuntimeException("cannot initialize");
56        }
57    }
58
59    /**
60     * Returns an index for accessing arrays in this class.
61     *
62     * @throws RuntimeException     if a given type is not a primitive type.
63     */
64    public static final int typeIndex(Class type) {
65        Class[] list = primitiveTypes;
66        int n = list.length;
67        for (int i = 0; i < n; i++)
68            if (list[i] == type)
69                return i;
70
71        throw new RuntimeException("bad type:" + type.getName());
72    }
73
74    /**
75     * <code>Class</code> objects representing primitive types.
76     */
77    public static final Class[] primitiveTypes = {
78        Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE,
79        Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
80    };
81
82    /**
83     * The fully-qualified names of wrapper classes for primitive types.
84     */
85    public static final String[] wrapperTypes = {
86        "java.lang.Boolean", "java.lang.Byte", "java.lang.Character",
87        "java.lang.Short", "java.lang.Integer", "java.lang.Long",
88        "java.lang.Float", "java.lang.Double", "java.lang.Void"
89    };
90
91    /**
92     * The descriptors of the constructors of wrapper classes.
93     */
94    public static final String[] wrapperDesc = {
95        "(Z)V", "(B)V", "(C)V", "(S)V", "(I)V", "(J)V",
96        "(F)V", "(D)V"
97    };
98
99    /**
100     * The names of methods for obtaining a primitive value
101     * from a wrapper object.  For example, <code>intValue()</code>
102     * is such a method for obtaining an integer value from a
103     * <code>java.lang.Integer</code> object.
104     */
105    public static final String[] unwarpMethods = {
106        "booleanValue", "byteValue", "charValue", "shortValue",
107        "intValue", "longValue", "floatValue", "doubleValue"
108    };
109
110    /**
111     * The descriptors of the unwrapping methods contained
112     * in <code>unwrapMethods</code>.
113     */
114    public static final String[] unwrapDesc = {
115        "()Z", "()B", "()C", "()S", "()I", "()J", "()F", "()D"
116    };
117
118    /**
119     * The data size of primitive types.  <code>long</code>
120     * and <code>double</code> are 2; the others are 1.
121     */
122    public static final int[] dataSize = {
123        1, 1, 1, 1, 1, 2, 1, 2
124    };
125
126    /**
127     * Loads a class file by a given class loader.
128     * This method uses a default protection domain for the class
129     * but it may not work with a security manager or a sigend jar file.
130     *
131     * @see #toClass(ClassFile,ClassLoader,ProtectionDomain)
132     */
133    public static Class toClass(ClassFile cf, ClassLoader loader)
134        throws CannotCompileException
135    {
136        return toClass(cf, loader, null);
137    }
138
139    /**
140     * Loads a class file by a given class loader.
141     *
142     * @param domain        if it is null, a default domain is used.
143     * @since 3.3
144     */
145    public static Class toClass(ClassFile cf, ClassLoader loader, ProtectionDomain domain)
146            throws CannotCompileException
147    {
148        try {
149            byte[] b = toBytecode(cf);
150            Method method;
151            Object[] args;
152            if (domain == null) {
153                method = defineClass1;
154                args = new Object[] { cf.getName(), b, new Integer(0),
155                        new Integer(b.length) };
156            }
157            else {
158                method = defineClass2;
159                args = new Object[] { cf.getName(), b, new Integer(0),
160                        new Integer(b.length), domain };
161            }
162
163            return toClass2(method, loader, args);
164        }
165        catch (RuntimeException e) {
166            throw e;
167        }
168        catch (java.lang.reflect.InvocationTargetException e) {
169            throw new CannotCompileException(e.getTargetException());
170        }
171        catch (Exception e) {
172            throw new CannotCompileException(e);
173        }
174    }
175
176    private static synchronized Class toClass2(Method method,
177                                        ClassLoader loader, Object[] args)
178        throws Exception
179    {
180        SecurityActions.setAccessible(method, true);
181        Class clazz = (Class)method.invoke(loader, args);
182        SecurityActions.setAccessible(method, false);
183        return clazz;
184    }
185
186    private static byte[] toBytecode(ClassFile cf) throws IOException {
187        ByteArrayOutputStream barray = new ByteArrayOutputStream();
188        DataOutputStream out = new DataOutputStream(barray);
189        try {
190            cf.write(out);
191        }
192        finally {
193            out.close();
194        }
195
196        return barray.toByteArray();
197    }
198
199    /**
200     * Writes a class file.
201     */
202    public static void writeFile(ClassFile cf, String directoryName)
203            throws CannotCompileException {
204        try {
205            writeFile0(cf, directoryName);
206        }
207        catch (IOException e) {
208            throw new CannotCompileException(e);
209        }
210    }
211
212    private static void writeFile0(ClassFile cf, String directoryName)
213            throws CannotCompileException, IOException {
214        String classname = cf.getName();
215        String filename = directoryName + File.separatorChar
216                + classname.replace('.', File.separatorChar) + ".class";
217        int pos = filename.lastIndexOf(File.separatorChar);
218        if (pos > 0) {
219            String dir = filename.substring(0, pos);
220            if (!dir.equals("."))
221                new File(dir).mkdirs();
222        }
223
224        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
225                new FileOutputStream(filename)));
226        try {
227            cf.write(out);
228        }
229        catch (IOException e) {
230            throw e;
231        }
232        finally {
233            out.close();
234        }
235    }
236}
237