001: package org.hibernate.bytecode.javassist;
002:
003: import java.lang.reflect.Method;
004: import java.lang.reflect.Modifier;
005: import java.security.ProtectionDomain;
006:
007: import javassist.CannotCompileException;
008: import javassist.bytecode.AccessFlag;
009: import javassist.bytecode.Bytecode;
010: import javassist.bytecode.ClassFile;
011: import javassist.bytecode.ConstPool;
012: import javassist.bytecode.MethodInfo;
013: import javassist.bytecode.Opcode;
014: import javassist.util.proxy.FactoryHelper;
015: import javassist.util.proxy.RuntimeSupport;
016:
017: /**
018: * A factory of bulk accessors.
019: *
020: * @author Muga Nishizawa
021: * @author modified by Shigeru Chiba
022: */
023: class BulkAccessorFactory {
024: private static final String PACKAGE_NAME_PREFIX = "org.javassist.tmp.";
025: private static final String BULKACESSOR_CLASS_NAME = BulkAccessor.class
026: .getName();
027: private static final String OBJECT_CLASS_NAME = Object.class
028: .getName();
029: private static final String GENERATED_GETTER_NAME = "getPropertyValues";
030: private static final String GENERATED_SETTER_NAME = "setPropertyValues";
031: private static final String GET_SETTER_DESC = "(Ljava/lang/Object;[Ljava/lang/Object;)V";
032: private static final String THROWABLE_CLASS_NAME = Throwable.class
033: .getName();
034: private static final String BULKEXCEPTION_CLASS_NAME = BulkAccessorException.class
035: .getName();
036: private static int counter = 0;
037:
038: private Class targetBean;
039: private String[] getterNames;
040: private String[] setterNames;
041: private Class[] types;
042: public String writeDirectory;
043:
044: BulkAccessorFactory(Class target, String[] getterNames,
045: String[] setterNames, Class[] types) {
046: this .targetBean = target;
047: this .getterNames = getterNames;
048: this .setterNames = setterNames;
049: this .types = types;
050: this .writeDirectory = null;
051: }
052:
053: BulkAccessor create() {
054: Method[] getters = new Method[getterNames.length];
055: Method[] setters = new Method[setterNames.length];
056: findAccessors(targetBean, getterNames, setterNames, types,
057: getters, setters);
058:
059: Class beanClass;
060: try {
061: ClassFile classfile = make(getters, setters);
062: ClassLoader loader = this .getClassLoader();
063: if (writeDirectory != null) {
064: FactoryHelper.writeFile(classfile, writeDirectory);
065: }
066:
067: beanClass = FactoryHelper.toClass(classfile, loader,
068: getDomain());
069: return (BulkAccessor) this .newInstance(beanClass);
070: } catch (Exception e) {
071: throw new BulkAccessorException(e.getMessage(), e);
072: }
073: }
074:
075: private ProtectionDomain getDomain() {
076: Class cl;
077: if (this .targetBean != null) {
078: cl = this .targetBean;
079: } else {
080: cl = this .getClass();
081: }
082: return cl.getProtectionDomain();
083: }
084:
085: private ClassFile make(Method[] getters, Method[] setters)
086: throws CannotCompileException {
087: String className = targetBean.getName();
088: // set the name of bulk accessor.
089: className = className + "_$$_bulkaccess_" + counter++;
090: if (className.startsWith("java.")) {
091: className = "org.javassist.tmp." + className;
092: }
093:
094: ClassFile classfile = new ClassFile(false, className,
095: BULKACESSOR_CLASS_NAME);
096: classfile.setAccessFlags(AccessFlag.PUBLIC);
097: addDefaultConstructor(classfile);
098: addGetter(classfile, getters);
099: addSetter(classfile, setters);
100: return classfile;
101: }
102:
103: private ClassLoader getClassLoader() {
104: if (targetBean != null
105: && targetBean.getName().equals(OBJECT_CLASS_NAME)) {
106: return targetBean.getClassLoader();
107: } else {
108: return getClass().getClassLoader();
109: }
110: }
111:
112: private Object newInstance(Class type) throws Exception {
113: BulkAccessor instance = (BulkAccessor) type.newInstance();
114: instance.target = targetBean;
115: int len = getterNames.length;
116: instance.getters = new String[len];
117: instance.setters = new String[len];
118: instance.types = new Class[len];
119: for (int i = 0; i < len; i++) {
120: instance.getters[i] = getterNames[i];
121: instance.setters[i] = setterNames[i];
122: instance.types[i] = types[i];
123: }
124:
125: return instance;
126: }
127:
128: /**
129: * Declares a constructor that takes no parameter.
130: *
131: * @param classfile
132: * @throws CannotCompileException
133: */
134: private void addDefaultConstructor(ClassFile classfile)
135: throws CannotCompileException {
136: ConstPool cp = classfile.getConstPool();
137: String cons_desc = "()V";
138: MethodInfo mi = new MethodInfo(cp, MethodInfo.nameInit,
139: cons_desc);
140:
141: Bytecode code = new Bytecode(cp, 0, 1);
142: // aload_0
143: code.addAload(0);
144: // invokespecial
145: code.addInvokespecial(BulkAccessor.class.getName(),
146: MethodInfo.nameInit, cons_desc);
147: // return
148: code.addOpcode(Opcode.RETURN);
149:
150: mi.setCodeAttribute(code.toCodeAttribute());
151: mi.setAccessFlags(AccessFlag.PUBLIC);
152: classfile.addMethod(mi);
153: }
154:
155: private void addGetter(ClassFile classfile, final Method[] getters)
156: throws CannotCompileException {
157: ConstPool cp = classfile.getConstPool();
158: int target_type_index = cp.addClassInfo(this .targetBean
159: .getName());
160: String desc = GET_SETTER_DESC;
161: MethodInfo mi = new MethodInfo(cp, GENERATED_GETTER_NAME, desc);
162:
163: Bytecode code = new Bytecode(cp, 6, 4);
164: /* | this | bean | args | raw bean | */
165: if (getters.length >= 0) {
166: // aload_1 // load bean
167: code.addAload(1);
168: // checkcast // cast bean
169: code.addCheckcast(this .targetBean.getName());
170: // astore_3 // store bean
171: code.addAstore(3);
172: for (int i = 0; i < getters.length; ++i) {
173: if (getters[i] != null) {
174: Method getter = getters[i];
175: // aload_2 // args
176: code.addAload(2);
177: // iconst_i // continue to aastore
178: code.addIconst(i); // growing stack is 1
179: Class returnType = getter.getReturnType();
180: int typeIndex = -1;
181: if (returnType.isPrimitive()) {
182: typeIndex = FactoryHelper.typeIndex(returnType);
183: // new
184: code
185: .addNew(FactoryHelper.wrapperTypes[typeIndex]);
186: // dup
187: code.addOpcode(Opcode.DUP);
188: }
189:
190: // aload_3 // load the raw bean
191: code.addAload(3);
192: String getter_desc = RuntimeSupport
193: .makeDescriptor(getter);
194: String getterName = getter.getName();
195: if (this .targetBean.isInterface()) {
196: // invokeinterface
197: code.addInvokeinterface(target_type_index,
198: getterName, getter_desc, 1);
199: } else {
200: // invokevirtual
201: code.addInvokevirtual(target_type_index,
202: getterName, getter_desc);
203: }
204:
205: if (typeIndex >= 0) { // is a primitive type
206: // invokespecial
207: code.addInvokespecial(
208: FactoryHelper.wrapperTypes[typeIndex],
209: MethodInfo.nameInit,
210: FactoryHelper.wrapperDesc[typeIndex]);
211: }
212:
213: // aastore // args
214: code.add(Opcode.AASTORE);
215: code.growStack(-3);
216: }
217: }
218: }
219: // return
220: code.addOpcode(Opcode.RETURN);
221:
222: mi.setCodeAttribute(code.toCodeAttribute());
223: mi.setAccessFlags(AccessFlag.PUBLIC);
224: classfile.addMethod(mi);
225: }
226:
227: private void addSetter(ClassFile classfile, final Method[] setters)
228: throws CannotCompileException {
229: ConstPool cp = classfile.getConstPool();
230: int target_type_index = cp.addClassInfo(this .targetBean
231: .getName());
232: String desc = GET_SETTER_DESC;
233: MethodInfo mi = new MethodInfo(cp, GENERATED_SETTER_NAME, desc);
234:
235: Bytecode code = new Bytecode(cp, 4, 6);
236: /* | this | bean | args | i | raw bean | exception | */
237: if (setters.length > 0) {
238: int start, end; // required to exception table
239: // iconst_0 // i
240: code.addIconst(0);
241: // istore_3 // store i
242: code.addIstore(3);
243: // aload_1 // load the bean
244: code.addAload(1);
245: // checkcast // cast the bean into a raw bean
246: code.addCheckcast(this .targetBean.getName());
247: // astore 4 // store the raw bean
248: code.addAstore(4);
249: /* current stack len = 0 */
250: // start region to handling exception (BulkAccessorException)
251: start = code.currentPc();
252: int lastIndex = 0;
253: for (int i = 0; i < setters.length; ++i) {
254: if (setters[i] != null) {
255: int diff = i - lastIndex;
256: if (diff > 0) {
257: // iinc 3, 1
258: code.addOpcode(Opcode.IINC);
259: code.add(3);
260: code.add(diff);
261: lastIndex = i;
262: }
263: }
264: /* current stack len = 0 */
265: // aload 4 // load the raw bean
266: code.addAload(4);
267: // aload_2 // load the args
268: code.addAload(2);
269: // iconst_i
270: code.addIconst(i);
271: // aaload
272: code.addOpcode(Opcode.AALOAD);
273: // checkcast
274: Class[] setterParamTypes = setters[i]
275: .getParameterTypes();
276: Class setterParamType = setterParamTypes[0];
277: if (setterParamType.isPrimitive()) {
278: // checkcast (case of primitive type)
279: // invokevirtual (case of primitive type)
280: this .addUnwrapper(classfile, code, setterParamType);
281: } else {
282: // checkcast (case of reference type)
283: code.addCheckcast(setterParamType.getName());
284: }
285: /* current stack len = 2 */
286: String rawSetterMethod_desc = RuntimeSupport
287: .makeDescriptor(setters[i]);
288: if (!this .targetBean.isInterface()) {
289: // invokevirtual
290: code.addInvokevirtual(target_type_index, setters[i]
291: .getName(), rawSetterMethod_desc);
292: } else {
293: // invokeinterface
294: Class[] params = setters[i].getParameterTypes();
295: int size;
296: if (params[0].equals(Double.TYPE)
297: || params[0].equals(Long.TYPE)) {
298: size = 3;
299: } else {
300: size = 2;
301: }
302:
303: code.addInvokeinterface(target_type_index,
304: setters[i].getName(), rawSetterMethod_desc,
305: size);
306: }
307: }
308:
309: // end region to handling exception (BulkAccessorException)
310: end = code.currentPc();
311: // return
312: code.addOpcode(Opcode.RETURN);
313: /* current stack len = 0 */
314: // register in exception table
315: int throwableType_index = cp
316: .addClassInfo(THROWABLE_CLASS_NAME);
317: code.addExceptionHandler(start, end, code.currentPc(),
318: throwableType_index);
319: // astore 5 // store exception
320: code.addAstore(5);
321: // new // BulkAccessorException
322: code.addNew(BULKEXCEPTION_CLASS_NAME);
323: // dup
324: code.addOpcode(Opcode.DUP);
325: // aload 5 // load exception
326: code.addAload(5);
327: // iload_3 // i
328: code.addIload(3);
329: // invokespecial // BulkAccessorException.<init>
330: String cons_desc = "(Ljava/lang/Throwable;I)V";
331: code.addInvokespecial(BULKEXCEPTION_CLASS_NAME,
332: MethodInfo.nameInit, cons_desc);
333: // athrow
334: code.addOpcode(Opcode.ATHROW);
335: } else {
336: // return
337: code.addOpcode(Opcode.RETURN);
338: }
339:
340: mi.setCodeAttribute(code.toCodeAttribute());
341: mi.setAccessFlags(AccessFlag.PUBLIC);
342: classfile.addMethod(mi);
343: }
344:
345: private void addUnwrapper(ClassFile classfile, Bytecode code,
346: Class type) {
347: int index = FactoryHelper.typeIndex(type);
348: String wrapperType = FactoryHelper.wrapperTypes[index];
349: // checkcast
350: code.addCheckcast(wrapperType);
351: // invokevirtual
352: code.addInvokevirtual(wrapperType,
353: FactoryHelper.unwarpMethods[index],
354: FactoryHelper.unwrapDesc[index]);
355: }
356:
357: private static void findAccessors(Class clazz,
358: String[] getterNames, String[] setterNames, Class[] types,
359: Method[] getters, Method[] setters) {
360: int length = types.length;
361: if (setterNames.length != length
362: || getterNames.length != length) {
363: throw new BulkAccessorException("bad number of accessors");
364: }
365:
366: Class[] getParam = new Class[0];
367: Class[] setParam = new Class[1];
368: for (int i = 0; i < length; i++) {
369: if (getterNames[i] != null) {
370: Method getter = findAccessor(clazz, getterNames[i],
371: getParam, i);
372: if (getter.getReturnType() != types[i]) {
373: throw new BulkAccessorException(
374: "wrong return type: " + getterNames[i], i);
375: }
376:
377: getters[i] = getter;
378: }
379:
380: if (setterNames[i] != null) {
381: setParam[0] = types[i];
382: setters[i] = findAccessor(clazz, setterNames[i],
383: setParam, i);
384: }
385: }
386: }
387:
388: private static Method findAccessor(Class clazz, String name,
389: Class[] params, int index) throws BulkAccessorException {
390: try {
391: Method method = clazz.getDeclaredMethod(name, params);
392: if (Modifier.isPrivate(method.getModifiers())) {
393: throw new BulkAccessorException("private property",
394: index);
395: }
396:
397: return method;
398: } catch (NoSuchMethodException e) {
399: throw new BulkAccessorException("cannot find an accessor",
400: index);
401: }
402: }
403: }
|