Open Source Repository

Home /hibernate/hibernate-3.2.6.ga | Repository Home


org/hibernate/bytecode/javassist/BulkAccessorFactory.java
package org.hibernate.bytecode.javassist;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;

import javassist.CannotCompileException;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.util.proxy.FactoryHelper;
import javassist.util.proxy.RuntimeSupport;

/**
 * A factory of bulk accessors.
 *
 @author Muga Nishizawa
 @author modified by Shigeru Chiba
 */
class BulkAccessorFactory {
  private static final String PACKAGE_NAME_PREFIX = "org.javassist.tmp.";
  private static final String BULKACESSOR_CLASS_NAME = BulkAccessor.class.getName();
  private static final String OBJECT_CLASS_NAME = Object.class.getName();
  private static final String GENERATED_GETTER_NAME = "getPropertyValues";
  private static final String GENERATED_SETTER_NAME = "setPropertyValues";
  private static final String GET_SETTER_DESC = "(Ljava/lang/Object;[Ljava/lang/Object;)V";
  private static final String THROWABLE_CLASS_NAME = Throwable.class.getName();
  private static final String BULKEXCEPTION_CLASS_NAME = BulkAccessorException.class.getName();
  private static int counter = 0;

  private Class targetBean;
  private String[] getterNames;
  private String[] setterNames;
  private Class[] types;
  public String writeDirectory;

  BulkAccessorFactory(
      Class target,
          String[] getterNames,
          String[] setterNames,
          Class[] types) {
    this.targetBean = target;
    this.getterNames = getterNames;
    this.setterNames = setterNames;
    this.types = types;
    this.writeDirectory = null;
  }

  BulkAccessor create() {
    Method[] getters = new Method[getterNames.length];
    Method[] setters = new Method[setterNames.length];
    findAccessorstargetBean, getterNames, setterNames, types, getters, setters );

    Class beanClass;
    try {
      ClassFile classfile = makegetters, setters );
      ClassLoader loader = this.getClassLoader();
      if writeDirectory != null ) {
        FactoryHelper.writeFileclassfile, writeDirectory );
      }

      beanClass = FactoryHelper.toClassclassfile, loader, getDomain() );
      return BulkAccessor this.newInstancebeanClass );
    }
    catch Exception e ) {
      throw new BulkAccessorExceptione.getMessage(), e );
    }
  }

  private ProtectionDomain getDomain() {
    Class cl;
    if this.targetBean != null ) {
      cl = this.targetBean;
    }
    else {
      cl = this.getClass();
    }
    return cl.getProtectionDomain();
  }

  private ClassFile make(Method[] getters, Method[] settersthrows CannotCompileException {
    String className = targetBean.getName();
    // set the name of bulk accessor.
    className = className + "_$$_bulkaccess_" + counter++;
    if className.startsWith"java." ) ) {
      className = "org.javassist.tmp." + className;
    }

    ClassFile classfile = new ClassFilefalse, className, BULKACESSOR_CLASS_NAME );
    classfile.setAccessFlagsAccessFlag.PUBLIC );
    addDefaultConstructorclassfile );
    addGetterclassfile, getters );
    addSetterclassfile, setters );
    return classfile;
  }

  private ClassLoader getClassLoader() {
    if targetBean != null && targetBean.getName().equalsOBJECT_CLASS_NAME ) ) {
      return targetBean.getClassLoader();
    }
    else {
      return getClass().getClassLoader();
    }
  }

  private Object newInstance(Class typethrows Exception {
    BulkAccessor instance = BulkAccessor type.newInstance();
    instance.target = targetBean;
    int len = getterNames.length;
    instance.getters = new String[len];
    instance.setters = new String[len];
    instance.types = new Class[len];
    for int i = 0; i < len; i++ ) {
      instance.getters[i= getterNames[i];
      instance.setters[i= setterNames[i];
      instance.types[i= types[i];
    }

    return instance;
  }

  /**
   * Declares a constructor that takes no parameter.
   *
   @param classfile
   @throws CannotCompileException
   */
  private void addDefaultConstructor(ClassFile classfilethrows CannotCompileException {
    ConstPool cp = classfile.getConstPool();
    String cons_desc = "()V";
    MethodInfo mi = new MethodInfocp, MethodInfo.nameInit, cons_desc );

    Bytecode code = new Bytecodecp, 0);
    // aload_0
    code.addAload);
    // invokespecial
    code.addInvokespecialBulkAccessor.class.getName(), MethodInfo.nameInit, cons_desc );
    // return
    code.addOpcodeOpcode.RETURN );

    mi.setCodeAttributecode.toCodeAttribute() );
    mi.setAccessFlagsAccessFlag.PUBLIC );
    classfile.addMethodmi );
  }

  private void addGetter(ClassFile classfile, final Method[] gettersthrows CannotCompileException {
    ConstPool cp = classfile.getConstPool();
    int target_type_index = cp.addClassInfothis.targetBean.getName() );
    String desc = GET_SETTER_DESC;
    MethodInfo mi = new MethodInfocp, GENERATED_GETTER_NAME, desc );

    Bytecode code = new Bytecodecp, 6);
    /* | this | bean | args | raw bean | */
    if getters.length >= ) {
      // aload_1 // load bean
      code.addAload);
      // checkcast // cast bean
      code.addCheckcastthis.targetBean.getName() );
      // astore_3 // store bean
      code.addAstore);
      for int i = 0; i < getters.length; ++i ) {
        if getters[i!= null ) {
          Method getter = getters[i];
          // aload_2 // args
          code.addAload);
          // iconst_i // continue to aastore
          code.addIconst)// growing stack is 1
          Class returnType = getter.getReturnType();
          int typeIndex = -1;
          if returnType.isPrimitive() ) {
            typeIndex = FactoryHelper.typeIndexreturnType );
            // new
            code.addNewFactoryHelper.wrapperTypes[typeIndex] );
            // dup
            code.addOpcodeOpcode.DUP );
          }

          // aload_3 // load the raw bean
          code.addAload);
          String getter_desc = RuntimeSupport.makeDescriptorgetter );
          String getterName = getter.getName();
          if this.targetBean.isInterface() ) {
            // invokeinterface
            code.addInvokeinterfacetarget_type_index, getterName, getter_desc, );
          }
          else {
            // invokevirtual
            code.addInvokevirtualtarget_type_index, getterName, getter_desc );
          }

          if typeIndex >= ) {       // is a primitive type
            // invokespecial
            code.addInvokespecial(
                FactoryHelper.wrapperTypes[typeIndex],
                    MethodInfo.nameInit,
                    FactoryHelper.wrapperDesc[typeIndex]
            );
          }

          // aastore // args
          code.addOpcode.AASTORE );
          code.growStack-);
        }
      }
    }
    // return
    code.addOpcodeOpcode.RETURN );

    mi.setCodeAttributecode.toCodeAttribute() );
    mi.setAccessFlagsAccessFlag.PUBLIC );
    classfile.addMethodmi );
  }

  private void addSetter(ClassFile classfile, final Method[] settersthrows CannotCompileException {
    ConstPool cp = classfile.getConstPool();
    int target_type_index = cp.addClassInfothis.targetBean.getName() );
    String desc = GET_SETTER_DESC;
    MethodInfo mi = new MethodInfocp, GENERATED_SETTER_NAME, desc );

    Bytecode code = new Bytecodecp, 4);
    /* | this | bean | args | i | raw bean | exception | */
    if setters.length > ) {
      int start, end; // required to exception table
      // iconst_0 // i
      code.addIconst);
      // istore_3 // store i
      code.addIstore);
      // aload_1 // load the bean
      code.addAload);
      // checkcast // cast the bean into a raw bean
      code.addCheckcastthis.targetBean.getName() );
      // astore 4 // store the raw bean
      code.addAstore);
      /* current stack len = 0 */
      // start region to handling exception (BulkAccessorException)
      start = code.currentPc();
      int lastIndex = 0;
      for int i = 0; i < setters.length; ++i ) {
        if setters[i!= null ) {
          int diff = i - lastIndex;
          if diff > ) {
            // iinc 3, 1
            code.addOpcodeOpcode.IINC );
            code.add);
            code.adddiff );
            lastIndex = i;
          }
        }
        /* current stack len = 0 */
        // aload 4 // load the raw bean
        code.addAload);
        // aload_2 // load the args
        code.addAload);
        // iconst_i
        code.addIconst);
        // aaload
        code.addOpcodeOpcode.AALOAD );
        // checkcast
        Class[] setterParamTypes = setters[i].getParameterTypes();
        Class setterParamType = setterParamTypes[0];
        if setterParamType.isPrimitive() ) {
          // checkcast (case of primitive type)
          // invokevirtual (case of primitive type)
          this.addUnwrapperclassfile, code, setterParamType );
        }
        else {
          // checkcast (case of reference type)
          code.addCheckcastsetterParamType.getName() );
        }
        /* current stack len = 2 */
        String rawSetterMethod_desc = RuntimeSupport.makeDescriptorsetters[i] );
        if !this.targetBean.isInterface() ) {
          // invokevirtual
          code.addInvokevirtualtarget_type_index, setters[i].getName(), rawSetterMethod_desc );
        }
        else {
          // invokeinterface
          Class[] params = setters[i].getParameterTypes();
          int size;
          if params[0].equalsDouble.TYPE || params[0].equalsLong.TYPE ) ) {
            size = 3;
          }
          else {
            size = 2;
          }

          code.addInvokeinterfacetarget_type_index, setters[i].getName(), rawSetterMethod_desc, size );
        }
      }

      // end region to handling exception (BulkAccessorException)
      end = code.currentPc();
      // return
      code.addOpcodeOpcode.RETURN );
      /* current stack len = 0 */
      // register in exception table
      int throwableType_index = cp.addClassInfoTHROWABLE_CLASS_NAME );
      code.addExceptionHandlerstart, end, code.currentPc(), throwableType_index );
      // astore 5 // store exception
      code.addAstore);
      // new // BulkAccessorException
      code.addNewBULKEXCEPTION_CLASS_NAME );
      // dup
      code.addOpcodeOpcode.DUP );
      // aload 5 // load exception
      code.addAload);
      // iload_3 // i
      code.addIload);
      // invokespecial // BulkAccessorException.<init>
      String cons_desc = "(Ljava/lang/Throwable;I)V";
      code.addInvokespecialBULKEXCEPTION_CLASS_NAME, MethodInfo.nameInit, cons_desc );
      // athrow
      code.addOpcodeOpcode.ATHROW );
    }
    else {
      // return
      code.addOpcodeOpcode.RETURN );
    }

    mi.setCodeAttributecode.toCodeAttribute() );
    mi.setAccessFlagsAccessFlag.PUBLIC );
    classfile.addMethodmi );
  }

  private void addUnwrapper(
      ClassFile classfile,
          Bytecode code,
          Class type) {
    int index = FactoryHelper.typeIndextype );
    String wrapperType = FactoryHelper.wrapperTypes[index];
    // checkcast
    code.addCheckcastwrapperType );
    // invokevirtual
    code.addInvokevirtualwrapperType, FactoryHelper.unwarpMethods[index], FactoryHelper.unwrapDesc[index] );
  }

  private static void findAccessors(
      Class clazz,
          String[] getterNames,
          String[] setterNames,
          Class[] types,
          Method[] getters,
          Method[] setters) {
    int length = types.length;
    if setterNames.length != length || getterNames.length != length ) {
      throw new BulkAccessorException"bad number of accessors" );
    }

    Class[] getParam = new Class[0];
    Class[] setParam = new Class[1];
    for int i = 0; i < length; i++ ) {
      if getterNames[i!= null ) {
        Method getter = findAccessorclazz, getterNames[i], getParam, i );
        if getter.getReturnType() != types[i] ) {
          throw new BulkAccessorException"wrong return type: " + getterNames[i], i );
        }

        getters[i= getter;
      }

      if setterNames[i!= null ) {
        setParam[0= types[i];
        setters[i= findAccessorclazz, setterNames[i], setParam, i );
      }
    }
  }

  private static Method findAccessor(
      Class clazz,
          String name,
          Class[] params,
          int indexthrows BulkAccessorException {
    try {
      Method method = clazz.getDeclaredMethodname, params );
      if Modifier.isPrivatemethod.getModifiers() ) ) {
        throw new BulkAccessorException"private property", index );
      }

      return method;
    }
    catch NoSuchMethodException e ) {
      throw new BulkAccessorException"cannot find an accessor", index );
    }
  }
}