org.batoo.jpa.core.impl.instance.Enhancer.java Source code

Java tutorial

Introduction

Here is the source code for org.batoo.jpa.core.impl.instance.Enhancer.java

Source

/*
 * Copyright (c) 2012-2013, Batu Alp Ceylan
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */

package org.batoo.jpa.core.impl.instance;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Set;

import javax.persistence.PersistenceException;
import javax.persistence.metamodel.EntityType;

import org.batoo.jpa.core.impl.manager.EntityManagerImpl;
import org.batoo.jpa.core.impl.manager.SessionImpl;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * The helper class to enhance a persistent class.
 * 
 * @author hceylan
 * @since 2.0.0
 */
public final class Enhancer {

    private static final Set<String> IGNORED_METHODS = Sets.newHashSet();

    static {
        for (final Method method : Object.class.getMethods()) {
            Enhancer.IGNORED_METHODS.add(method.getName());
        }
    }

    /**
     * The enhanced suffix for the class
     */
    public static final String SUFFIX_ENHANCED = "$Enhanced";

    private static final String THIS = "this";

    private static final String CLASS_ENHANCED_SUFFIX = Enhancer.SUFFIX_ENHANCED;

    private static final String CONSTRUCTOR_INIT = "<init>";

    private static final String FIELD_SERIAL_VERSION_UID = "serialVersionUID";
    private static final String FIELD_ENHANCED_INITIALIZED = "__enhanced_$$__initialized";
    private static final String FIELD_ENHANCED_INTERNAL = "__enhanced_$$__internal";
    private static final String FIELD_ENHANCED_SESSION = "__enhanced_$$__session";
    private static final String FIELD_ENHANCED_TYPE = "__enhanced_$$__type";
    private static final String FIELD_ENHANCED_ID = "__enhanced_$$__id";
    private static final String FIELD_ENHANCED_MANAGED_INSTANCE = "__enhanced_$$__managedInstance";

    private static final String METHOD_ENHANCED_IS_INITIALIZED = "__enhanced__$$__isInitialized";
    private static final String METHOD_ENHANCED_SET_INITIALIZED = "__enhanced__$$__setInitialized";
    private static final String METHOD_ENHANCED_CHECK = "__enhanced_$$__check";
    private static final String METHOD_GET_ENTITY_MANAGER = "getEntityManager";
    private static final String METHOD_ENHANCED_GET_MANAGED_INSTANCE = "__enhanced__$$__getManagedInstance";
    private static final String METHOD_ENHANCED_SET_MANAGED_INSTANCE = "__enhanced__$$__setManagedInstance";
    private static final String METHOD_ENHANCED_SET_INTERNAL = "__enhanced__$$__setInternalCall";
    private static final String METHOD_FIND = "find";
    private static final String METHOD_CHANGED = "changed";

    private static final String DESCRIPTOR_BOOLEAN = Type.getDescriptor(Boolean.TYPE);
    private static final String DESCRIPTOR_MANAGED_INSTANCE = Type.getDescriptor(ManagedInstance.class);
    private static final String DESCRIPTOR_OBJECT = Type.getDescriptor(Object.class);
    private static final String DESCRIPTOR_SESSION = Type.getDescriptor(SessionImpl.class);
    private static final String DESCRIPTOR_CLASS = Type.getDescriptor(Class.class);

    private static final String INTERNAL_PERSISTENCE_EXCEPTION = Type.getInternalName(PersistenceException.class);
    private static final String INTERNAL_SESSION = Type.getInternalName(SessionImpl.class);
    private static final String INTERNAL_ENTITY_MANAGER = Type.getInternalName(EntityManagerImpl.class);
    private static final String INTERNAL_MANAGED_INSTANCE = Type.getInternalName(ManagedInstance.class);

    /**
     * Returns the enhanced class bytecode.
     * 
     * @param clazz
     *            the class to enhance
     * @return the enhanced class
     * @throws Exception
     *             thrown in case of an error
     * 
     * @since 2.0.0
     */
    //@formatter:off
    public static byte[] create(Class<?> clazz) throws Exception {

        final String enhancingClassName = Type.getInternalName(clazz);
        final String enhancedClassName = enhancingClassName + Enhancer.SUFFIX_ENHANCED;
        final String descEnhancer = Enhancer.makeClassDesc(enhancedClassName);

        final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, enhancedClassName, null, enhancingClassName,
                new String[] { Type.getInternalName(EnhancedInstance.class) });

        // Field: serialVersionUID
        cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
                Enhancer.FIELD_SERIAL_VERSION_UID, Type.getDescriptor(Long.TYPE), null, Long.valueOf(1L))
                .visitEnd();

        // Container fields
        cw.visitField(Opcodes.ACC_PRIVATE, Enhancer.FIELD_ENHANCED_INITIALIZED, Enhancer.DESCRIPTOR_BOOLEAN, null,
                null).visitEnd();
        cw.visitField(Opcodes.ACC_PRIVATE, Enhancer.FIELD_ENHANCED_INTERNAL, Enhancer.DESCRIPTOR_BOOLEAN, null,
                null).visitEnd();
        cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_TRANSIENT, Enhancer.FIELD_ENHANCED_ID,
                Enhancer.DESCRIPTOR_OBJECT, null, null).visitEnd();
        cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_TRANSIENT, Enhancer.FIELD_ENHANCED_TYPE,
                Enhancer.DESCRIPTOR_CLASS, null, null).visitEnd();
        cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_TRANSIENT,
                Enhancer.FIELD_ENHANCED_SESSION, Enhancer.DESCRIPTOR_SESSION, null, null).visitEnd();
        cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_TRANSIENT, Enhancer.FIELD_ENHANCED_MANAGED_INSTANCE,
                Enhancer.DESCRIPTOR_MANAGED_INSTANCE, null, null).visitEnd();

        // Constructors
        Enhancer.createNoArgConstructor(enhancingClassName, enhancedClassName, descEnhancer, cw);
        Enhancer.createContainerConstructor(enhancingClassName, enhancedClassName, descEnhancer, cw);
        Enhancer.createMethodIsInitialized(enhancedClassName, descEnhancer, cw);
        Enhancer.createMethodSetInitialized(enhancedClassName, descEnhancer, cw);
        Enhancer.createMethodCheck(enhancedClassName, descEnhancer, cw);
        Enhancer.createMethodGetManagedInstance(enhancedClassName, descEnhancer, cw);
        Enhancer.createMethodSetManagedInstance(enhancedClassName, descEnhancer, cw);
        Enhancer.createMethodSetInternal(enhancedClassName, descEnhancer, cw);

        final Map<String, Method> methods = Maps.newHashMap();

        Class<?> currentClass = clazz;
        while (currentClass != Object.class) { // we are not interested in Object.class
            for (final Method method : currentClass.getDeclaredMethods()) {
                int modifiers = method.getModifiers();

                if (Modifier.isAbstract(modifiers) || Modifier.isStatic(modifiers) || Modifier.isPrivate(modifiers)
                        || method.isSynthetic() || method.isBridge()) {
                    continue;
                }

                // Filter out the details that we are not interested
                modifiers &= Modifier.ABSTRACT;
                modifiers &= Modifier.FINAL;
                modifiers &= Modifier.NATIVE;
                modifiers &= Modifier.PRIVATE;
                modifiers &= Modifier.PROTECTED;
                modifiers &= Modifier.STATIC;
                modifiers &= Modifier.STRICT;

                if ((modifiers == Modifier.PUBLIC) || (modifiers == 0)) {
                    // we are not interested in the return type to omit the overridden methods
                    final String desc = method.getName()
                            + Enhancer.makeDescription(Void.TYPE, method.getParameterTypes());

                    if (methods.get(desc) == null) {
                        methods.put(desc, method);
                    }
                }
            }

            currentClass = currentClass.getSuperclass();
        }

        for (final Method method : methods.values()) {
            if (!Enhancer.IGNORED_METHODS.contains(method.getName())) {
                Enhancer.createOverrriddenMethod(enhancingClassName, enhancedClassName, descEnhancer, cw, method);
            }
        }

        cw.visitEnd();

        return cw.toByteArray();
    }

    private static void createContainerConstructor(final String enhancingClassName, final String enhancedClassName,
            final String descEnhancer, final ClassWriter cw) {
        final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, Enhancer.CONSTRUCTOR_INIT,
                Enhancer.makeDescription(Void.TYPE, Class.class, SessionImpl.class, Object.class, Boolean.TYPE),
                null, null);
        mv.visitCode();

        final Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, enhancingClassName, Enhancer.CONSTRUCTOR_INIT,
                Enhancer.makeDescription(Void.TYPE));

        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_TYPE,
                Enhancer.DESCRIPTOR_CLASS);

        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.ALOAD, 2);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_SESSION,
                Enhancer.DESCRIPTOR_SESSION);

        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.ALOAD, 3);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_ID,
                Enhancer.DESCRIPTOR_OBJECT);

        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.ILOAD, 4);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_INITIALIZED,
                Enhancer.DESCRIPTOR_BOOLEAN);

        mv.visitInsn(Opcodes.RETURN);

        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, l0, l1, 0);
        mv.visitLocalVariable("type", Enhancer.DESCRIPTOR_CLASS, null, l0, l1, 1);
        mv.visitLocalVariable("session", Enhancer.DESCRIPTOR_SESSION, null, l0, l1, 2);
        mv.visitLocalVariable("id", Enhancer.DESCRIPTOR_OBJECT, null, l0, l1, 3);
        mv.visitLocalVariable("initialized", Enhancer.DESCRIPTOR_BOOLEAN, null, l0, l1, 4);
        mv.visitMaxs(0, 0);

        mv.visitEnd();
    }

    private static void createMethodCheck(final String enhancedClassName, final String descEnhancer,
            final ClassWriter cw) {
        final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PRIVATE, Enhancer.METHOD_ENHANCED_CHECK,
                Enhancer.makeDescription(Void.TYPE), null, null);
        mv.visitCode();

        final Label lCheckInternal = new Label();
        final Label lCheckInitialized = new Label();
        final Label lReturn = new Label();
        final Label lFind = new Label();
        final Label lInitialized = new Label();
        final Label lChanged = new Label();
        final Label lOut = new Label();

        // if (!this.__enhanced__$$__internal) { return }
        mv.visitLabel(lCheckInternal);
        mv.visitFrame(Opcodes.F_NEW, 1, new Object[] { enhancedClassName }, 0, new Object[] {});
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_INTERNAL,
                Enhancer.DESCRIPTOR_BOOLEAN);
        mv.visitJumpInsn(Opcodes.IFEQ, lCheckInitialized);
        mv.visitInsn(Opcodes.RETURN);

        // if (!this.__enhanced__$$__initialized) {
        mv.visitLabel(lCheckInitialized);
        mv.visitFrame(Opcodes.F_NEW, 1, new Object[] { enhancedClassName }, 0, new Object[] {});
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_INITIALIZED,
                Enhancer.DESCRIPTOR_BOOLEAN);
        mv.visitJumpInsn(Opcodes.IFNE, lChanged);

        //     if (this.__enhanced_$$__session == null)
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_SESSION,
                Enhancer.DESCRIPTOR_SESSION);
        mv.visitJumpInsn(Opcodes.IFNONNULL, lFind);

        //         throw new PersistenceException("No session to initialize the instance");
        mv.visitTypeInsn(Opcodes.NEW, Enhancer.INTERNAL_PERSISTENCE_EXCEPTION);
        mv.visitInsn(Opcodes.DUP);
        mv.visitLdcInsn("No session to initialize the instance");
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Enhancer.INTERNAL_PERSISTENCE_EXCEPTION,
                Enhancer.CONSTRUCTOR_INIT, Enhancer.makeDescription(Void.TYPE, String.class));
        mv.visitInsn(Opcodes.ATHROW);

        //       this.__enhanced_$$__session.getEntityManager().find(this.__enhanced_$$__type, this.__enhanced__$$__id);
        mv.visitLabel(lFind);
        mv.visitFrame(Opcodes.F_NEW, 1, new Object[] { enhancedClassName }, 0, new Object[] {});
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_SESSION,
                Enhancer.DESCRIPTOR_SESSION);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Enhancer.INTERNAL_SESSION, Enhancer.METHOD_GET_ENTITY_MANAGER,
                Enhancer.makeDescription(EntityManagerImpl.class));
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_TYPE,
                Enhancer.DESCRIPTOR_CLASS);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_ID,
                Enhancer.DESCRIPTOR_OBJECT);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Enhancer.INTERNAL_ENTITY_MANAGER, Enhancer.METHOD_FIND,
                Enhancer.makeDescription(Object.class, Class.class, Object.class));
        mv.visitInsn(Opcodes.POP);

        //   this.__enhanced__$$__initialized = true;
        mv.visitLabel(lInitialized);
        mv.visitFrame(Opcodes.F_NEW, 1, new Object[] { enhancedClassName }, 0, new Object[] {});
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitInsn(Opcodes.ICONST_1);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_INITIALIZED,
                Enhancer.DESCRIPTOR_BOOLEAN);

        // if (this.__enhanced_$$__session != null)
        mv.visitLabel(lChanged);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_SESSION,
                Enhancer.DESCRIPTOR_SESSION);
        mv.visitJumpInsn(Opcodes.IFNULL, lReturn);

        //     this.__enhanced__$$__managedInstance.changed();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_MANAGED_INSTANCE,
                Enhancer.DESCRIPTOR_MANAGED_INSTANCE);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Enhancer.INTERNAL_MANAGED_INSTANCE, Enhancer.METHOD_CHANGED,
                Enhancer.makeDescription(Void.TYPE));

        // return;
        mv.visitLabel(lReturn);
        mv.visitFrame(Opcodes.F_NEW, 1, new Object[] { enhancedClassName }, 0, new Object[] {});
        mv.visitInsn(Opcodes.RETURN);

        mv.visitLabel(lOut);
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, lCheckInternal, lOut, 0);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private static void createMethodGetManagedInstance(final String enhancedClassName, final String descEnhancer,
            final ClassWriter cw) {
        final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, Enhancer.METHOD_ENHANCED_GET_MANAGED_INSTANCE,
                Enhancer.makeDescription(ManagedInstance.class), null, null);
        mv.visitCode();

        final Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_MANAGED_INSTANCE,
                Enhancer.DESCRIPTOR_MANAGED_INSTANCE);
        mv.visitInsn(Opcodes.ARETURN);

        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, l0, l1, 0);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private static void createMethodIsInitialized(final String enhancedClassName, final String descEnhancer,
            final ClassWriter cw) {
        final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, Enhancer.METHOD_ENHANCED_IS_INITIALIZED,
                Enhancer.makeDescription(Boolean.TYPE), null, null);
        mv.visitCode();

        final Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_INITIALIZED,
                Enhancer.DESCRIPTOR_BOOLEAN);
        mv.visitInsn(Opcodes.IRETURN);

        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, l0, l1, 0);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private static void createMethodSetInitialized(final String enhancedClassName, final String descEnhancer,
            final ClassWriter cw) {
        final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, Enhancer.METHOD_ENHANCED_SET_INITIALIZED,
                Enhancer.makeDescription(Void.TYPE), null, null);
        mv.visitCode();

        final Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitInsn(Opcodes.ICONST_1);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_INITIALIZED,
                Enhancer.DESCRIPTOR_BOOLEAN);

        mv.visitInsn(Opcodes.RETURN);

        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, l0, l1, 0);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private static void createMethodSetInternal(final String enhancedClassName, final String descEnhancer,
            final ClassWriter cw) {
        final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, Enhancer.METHOD_ENHANCED_SET_INTERNAL,
                Enhancer.makeDescription(Void.TYPE, Boolean.TYPE), null, null);
        mv.visitCode();

        final Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.ILOAD, 1);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_INTERNAL,
                Enhancer.DESCRIPTOR_BOOLEAN);

        mv.visitInsn(Opcodes.RETURN);

        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, l0, l1, 0);
        mv.visitLocalVariable("internal", Enhancer.DESCRIPTOR_BOOLEAN, null, l0, l1, 1);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        //      final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "__enhanced__$$__setInternalCall", "(Z)V", null, null);
        //      mv.visitCode();
        //      final Label l0 = new Label();
        //      mv.visitLabel(l0);
        //      mv.visitLineNumber(81, l0);
        //      mv.visitVarInsn(Opcodes.ALOAD, 0);
        //      mv.visitVarInsn(Opcodes.ILOAD, 1);
        //      mv.visitFieldInsn(Opcodes.PUTFIELD, descEnhancer, Enhancer.FIELD_ENHANCED_INTERNAL, Enhancer.DESCRIPTOR_BOOLEAN);
        //      final Label l1 = new Label();
        //      mv.visitLabel(l1);
        //      mv.visitLineNumber(82, l1);
        //      mv.visitInsn(Opcodes.RETURN);
        //      final Label l2 = new Label();
        //      mv.visitLabel(l2);
        //      mv.visitLocalVariable("this", descEnhancer, null, l0, l2, 0);
        //      mv.visitLocalVariable("internal", "Z", null, l0, l2, 1);
        //      mv.visitMaxs(2, 2);
        //      mv.visitEnd();
    }

    private static void createMethodSetManagedInstance(final String enhancedClassName, final String descEnhancer,
            final ClassWriter cw) {
        final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, Enhancer.METHOD_ENHANCED_SET_MANAGED_INSTANCE,
                Enhancer.makeDescription(Void.TYPE, ManagedInstance.class), null, null);
        mv.visitCode();

        final Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_MANAGED_INSTANCE,
                Enhancer.DESCRIPTOR_MANAGED_INSTANCE);

        mv.visitInsn(Opcodes.RETURN);

        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, l0, l1, 0);
        mv.visitLocalVariable("instance", Enhancer.DESCRIPTOR_MANAGED_INSTANCE, null, l0, l1, 1);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private static void createNoArgConstructor(final String enhancingClassName, final String enhancedClassName,
            final String descEnhancer, final ClassWriter cw) {
        final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, Enhancer.CONSTRUCTOR_INIT,
                Enhancer.makeDescription(Void.TYPE), null, null);
        mv.visitCode();

        final Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, enhancingClassName, Enhancer.CONSTRUCTOR_INIT,
                Enhancer.makeDescription(Void.TYPE));

        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitInsn(Opcodes.ACONST_NULL);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_ID,
                Enhancer.DESCRIPTOR_OBJECT);

        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitInsn(Opcodes.ACONST_NULL);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_TYPE,
                Enhancer.DESCRIPTOR_CLASS);

        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitInsn(Opcodes.ACONST_NULL);
        mv.visitFieldInsn(Opcodes.PUTFIELD, enhancedClassName, Enhancer.FIELD_ENHANCED_SESSION,
                Enhancer.DESCRIPTOR_SESSION);

        mv.visitInsn(Opcodes.RETURN);

        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, l0, l1, 0);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
    //@formatter:on

    private static void createOverrriddenMethod(final String enhancingClassName, final String enhancedClassName,
            final String descEnhancer, final ClassWriter cw, Method method) {
        final String methodDescription = Enhancer.makeDescription(method.getReturnType(),
                method.getParameterTypes());

        // TODO Exception types
        for (int i = 0; i < method.getExceptionTypes().length; i++) {
        }

        final MethodVisitor mv = cw.visitMethod(method.getModifiers(), method.getName(), methodDescription, null,
                null);
        mv.visitCode();

        // this.__enhanced_$$__check();
        final Label lCheck = new Label();
        mv.visitLabel(lCheck);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, enhancedClassName, Enhancer.METHOD_ENHANCED_CHECK,
                Enhancer.makeDescription(Void.TYPE));

        mv.visitVarInsn(Opcodes.ALOAD, 0); // load this

        // infer the method parameters
        for (int i = 0, r = 1; i < method.getParameterTypes().length; i++, r++) {
            final Class<?> paramClass = method.getParameterTypes()[i];
            mv.visitVarInsn(Enhancer.getLoadType(paramClass), r); // load parameter

            if ((paramClass == Double.TYPE) || (paramClass == Long.TYPE)) {
                r++;
            }
        }

        // super.method(...);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, enhancingClassName, method.getName(), methodDescription);

        if (method.getReturnType() != Void.TYPE) {
            // non void return
            mv.visitInsn(Enhancer.getReturnType(method.getReturnType()));
        } else {
            // void return
            mv.visitInsn(Opcodes.RETURN);
        }

        final Label lOut = new Label();
        mv.visitLabel(lOut);
        Enhancer.registerLocals(descEnhancer, method, mv, lCheck, lOut);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    /**
     * Enhances a type.
     * 
     * @param <T>
     *            the type of the entity
     * @param type
     *            the type for the class
     * @return the enhanced class
     * @throws Exception
     *             thrown if the enhancement fails
     * 
     * @since 2.0.0
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> enhance(EntityType<T> type) throws Exception {
        final Class<T> javaType = type.getJavaType();
        final ClassLoader classLoader = javaType.getClassLoader();
        final String className = type.getJavaType().getName() + Enhancer.CLASS_ENHANCED_SUFFIX;

        // try if loaded
        final Class<T> enhancedClass = (Class<T>) Enhancer.tryLoadClass(classLoader, className);
        if (enhancedClass != null) {
            return enhancedClass;
        }

        return Enhancer.enhance0(javaType, classLoader, className);
    }

    private synchronized static <T> Class<T> enhance0(final Class<T> javaType, final ClassLoader classLoader,
            final String className) throws Exception {
        final byte[] byteCode = Enhancer.create(javaType);

        final Class<T> enhancedClass = Enhancer.loadClass(classLoader, byteCode, className);

        return enhancedClass;
    }

    private static void getDescriptor(final StringBuffer buf, final Class<?> c) {
        Class<?> d = c;
        while (true) {
            if (d.isPrimitive()) {
                char car;
                if (d == Integer.TYPE) {
                    car = 'I';
                } else if (d == Void.TYPE) {
                    car = 'V';
                } else if (d == Boolean.TYPE) {
                    car = 'Z';
                } else if (d == Byte.TYPE) {
                    car = 'B';
                } else if (d == Character.TYPE) {
                    car = 'C';
                } else if (d == Short.TYPE) {
                    car = 'S';
                } else if (d == Double.TYPE) {
                    car = 'D';
                } else if (d == Float.TYPE) {
                    car = 'F';
                } else /* if (d == Long.TYPE) */ {
                    car = 'J';
                }
                buf.append(car);
                return;
            } else if (d.isArray()) {
                buf.append('[');
                d = d.getComponentType();
            } else {
                buf.append('L');
                final String name = d.getName();
                final int len = name.length();
                for (int i = 0; i < len; ++i) {
                    final char car = name.charAt(i);
                    buf.append(car == '.' ? '/' : car);
                }
                buf.append(';');
                return;
            }
        }
    }

    private static int getLoadType(Class<?> paramClass) {
        if (!paramClass.isPrimitive() || paramClass.isArray()) {
            return Opcodes.ALOAD;
        }

        if (Long.TYPE == paramClass) {
            return Opcodes.LLOAD;
        }

        if (Float.TYPE == paramClass) {
            return Opcodes.FLOAD;
        }

        if (Double.TYPE == paramClass) {
            return Opcodes.DLOAD;
        }

        return Opcodes.ILOAD;
    }

    private static int getReturnType(Class<?> paramClass) {
        if (!paramClass.isPrimitive() || paramClass.isArray()) {
            return Opcodes.ARETURN;
        }

        if (Long.TYPE == paramClass) {
            return Opcodes.LRETURN;
        }

        if (Float.TYPE == paramClass) {
            return Opcodes.FRETURN;
        }

        if (Double.TYPE == paramClass) {
            return Opcodes.DRETURN;
        }

        return Opcodes.IRETURN;
    }

    /**
     * Loads the class.
     * 
     * @param classLoader
     *            the class loader
     * @param byteCode
     *            the bytecode
     * @param className
     *            the name of the class
     * @param <T>
     *            the type of the class
     * @return the loaded class
     * @throws Exception
     *             if class cannot be loaded
     * 
     * @since 2.0.0
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> loadClass(ClassLoader classLoader, byte[] byteCode, String className)
            throws Exception {
        final Class<?> cls = Class.forName("java.lang.ClassLoader");
        final java.lang.reflect.Method method = cls.getDeclaredMethod("defineClass",
                new Class[] { String.class, byte[].class, Integer.TYPE, Integer.TYPE });

        // protected method invocation
        method.setAccessible(true);
        try {
            final Object[] args = new Object[] { className, byteCode, Integer.valueOf(0),
                    Integer.valueOf(byteCode.length) };
            return (Class<T>) method.invoke(classLoader, args);
        } finally {
            method.setAccessible(false);
        }
    }

    private static String makeClassDesc(String className) {
        return "L" + className + ";";
    }

    private static String makeDescription(Class<?> returnType, Class<?>... parameters) {
        final StringBuffer buf = new StringBuffer();
        buf.append('(');
        for (int i = 0; i < parameters.length; ++i) {
            Enhancer.getDescriptor(buf, parameters[i]);
        }
        buf.append(')');
        Enhancer.getDescriptor(buf, returnType);
        return buf.toString();
    }

    private static void registerLocals(final String descEnhancer, Method method, final MethodVisitor mv,
            final Label l0, final Label l) {
        mv.visitLocalVariable(Enhancer.THIS, descEnhancer, null, l0, l, 0);
        for (int i = 0, r = 1; i < method.getParameterTypes().length; i++, r++) {
            final Class<?> paramClass = method.getParameterTypes()[i];
            mv.visitLocalVariable("arg" + i, Type.getDescriptor(paramClass), null, l0, l, r);

            if ((paramClass == Double.TYPE) || (paramClass == Long.TYPE)) {
                r++;
            }
        }
    }

    private static Class<?> tryLoadClass(final ClassLoader classLoader, final String className) {
        final ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(classLoader);
            return Class.forName(className);
        } catch (final ClassNotFoundException e) {
            return null;
        } finally {
            Thread.currentThread().setContextClassLoader(oldClassLoader);
        }
    }
}