Package org.eclipse.persistence.internal.weaving

Source Code of org.eclipse.persistence.internal.weaving.ClassWeaver

/*******************************************************************************
* Copyright (c) 1998, 2009 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
*     Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/ 
package org.eclipse.persistence.internal.weaving;

import java.util.*;

import org.eclipse.persistence.internal.libraries.asm.*;
import org.eclipse.persistence.internal.libraries.asm.commons.*;
import org.eclipse.persistence.internal.libraries.asm.attrs.RuntimeVisibleAnnotations;
import org.eclipse.persistence.internal.libraries.asm.attrs.Annotation;

/**
* INTERNAL:
* Weaves classes to allow them to support TopLink indirection.
* Classes are weaved to add a variable of type ValueHolderInterface for each attribute
* that uses indirection.  In addition, access methods are added for the new variable.
* Also, triggers the process of weaving the methods of the class. 
* @see org.eclipse.persistence.internal.weaving.MethodWeaver
*/

public class ClassWeaver extends ClassAdapter implements Constants {

    // PersistenceWeaved
    public static final String PERSISTENCE_WEAVED_SHORT_SIGNATURE = "org/eclipse/persistence/internal/weaving/PersistenceWeaved";
   
    // ValueHolders
    public static final String TW_LAZY_SHORT_SIGNATURE = "org/eclipse/persistence/internal/weaving/PersistenceWeavedLazy";
    public static final String VHI_CLASSNAME = "org.eclipse.persistence.indirection.WeavedAttributeValueHolderInterface";
    public static final String VH_SHORT_SIGNATURE = "org/eclipse/persistence/indirection/ValueHolder";
    public static final String VHI_SHORT_SIGNATURE = "org/eclipse/persistence/indirection/WeavedAttributeValueHolderInterface";
    public static final String VHI_SIGNATURE = "L" + VHI_SHORT_SIGNATURE +";";
   
    // Change tracking
    public static final String TW_CT_SHORT_SIGNATURE = "org/eclipse/persistence/internal/weaving/PersistenceWeavedChangeTracking";
    public static final String PCL_SHORT_SIGNATURE = "java/beans/PropertyChangeListener";
    public static final String PCL_SIGNATURE = "L" + PCL_SHORT_SIGNATURE +";";
    public static final String CT_SHORT_SIGNATURE = "org/eclipse/persistence/descriptors/changetracking/ChangeTracker";
    public static final String PCE_SHORT_SIGNATURE = "java/beans/PropertyChangeEvent";
    public static final String PCE_SIGNATURE = "L" + PCE_SHORT_SIGNATURE +";";
   
    // PersistenceEntity
    public static final String PERSISTENCE_ENTITY_SHORT_SIGNATURE = "org/eclipse/persistence/internal/descriptors/PersistenceEntity";
    public static final String PERSISTENCE_OBJECT_SHORT_SIGNATURE = "org/eclipse/persistence/internal/descriptors/PersistenceObject";
    public static final String PERSISTENCE_OBJECT_SIGNATURE = "L" + PERSISTENCE_OBJECT_SHORT_SIGNATURE + ";";
    public static final String VECTOR_SIGNATURE = "Ljava/util/Vector;";
    public static final String CACHEKEY_SIGNATURE = "Lorg/eclipse/persistence/internal/identitymaps/CacheKey;";
   
    // Fetch groups
    public static final String WEAVED_FETCHGROUPS_SHORT_SIGNATURE = "org/eclipse/persistence/internal/weaving/PersistenceWeavedFetchGroups";
    public static final String FETCHGROUP_TRACKER_SHORT_SIGNATURE = "org/eclipse/persistence/queries/FetchGroupTracker";
    public static final String FETCHGROUP_SHORT_SIGNATURE = "org/eclipse/persistence/queries/FetchGroup";
    public static final String FETCHGROUP_SIGNATURE = "Lorg/eclipse/persistence/queries/FetchGroup;";
    public static final String SESSION_SIGNATURE = "Lorg/eclipse/persistence/sessions/Session;";
    public static final String PBOOLEAN_SIGNATURE = "Z";
    public static final String LONG_SIGNATURE = "J";
   
    // Cloneable
    public static final String CLONEABLE_SHORT_SIGNATURE = "java/lang/Cloneable";
   
    /** Store if JAXB is no the classpath. */
    protected static Boolean isJAXBOnPath;
   
    /** Stores information on the class gathered from the temp class loader and descriptor. */
    protected ClassDetails classDetails;
    /** Used to generate the serialization serial UUID based on the original class. */
    protected SerialVersionUIDAdder uuidGenerator;
   
    // Keep track of what was weaved.
    protected boolean alreadyWeaved = false;
    public boolean weaved = false;
    public boolean weavedLazy = false;
    public boolean weavedPersistenceEntity = false;
    public boolean weavedChangeTracker = false;
    public boolean weavedFetchGroups = false;
   
    /**
     * Used for primitive conversion.  Returns the name of the class that wraps a given type.
     */
    public static String wrapperFor(int sort) {
        switch (sort) {
            case Type.BOOLEAN:
                return "java/lang/Boolean";
            case Type.BYTE:
                return "java/lang/Byte";
            case Type.CHAR:
                return "java/lang/Character";
            case Type.SHORT:
                return "java/lang/Short";
            case Type.INT:
                return "java/lang/Integer";
            case Type.FLOAT:
                return "java/lang/Float";
            case Type.LONG:
                return "java/lang/Long";
            case Type.DOUBLE:
                return "java/lang/Double";
        }
        return null;
    }
   
    /**
     * Used for primitive conversion.  Returns the name conversion method for the given type.
     */
    public static void unwrapPrimitive(AttributeDetails attribute, CodeVisitor visitor) {
        String wrapper = wrapperFor(attribute.getReferenceClassType().getSort());
        switch (attribute.getReferenceClassType().getSort()) {
            case Type.BOOLEAN:
                visitor.visitMethodInsn(INVOKEVIRTUAL, wrapper, "booleanValue", "()Z");
                return;
            case Type.BYTE:
                visitor.visitMethodInsn(INVOKEVIRTUAL, wrapper, "byteValue", "()B");
                return;
            case Type.CHAR:
                visitor.visitMethodInsn(INVOKEVIRTUAL, wrapper, "charValue", "()C");
                return;
            case Type.SHORT:
                visitor.visitMethodInsn(INVOKEVIRTUAL, wrapper, "shortValue", "()S");
                return;
            case Type.INT:
                visitor.visitMethodInsn(INVOKEVIRTUAL, wrapper, "intValue", "()I");
                return;
            case Type.FLOAT:
                visitor.visitMethodInsn(INVOKEVIRTUAL, wrapper, "floatValue", "()F");
                return;
            case Type.LONG:
                visitor.visitMethodInsn(INVOKEVIRTUAL, wrapper, "longValue", "()J");
                return;
            case Type.DOUBLE:
                visitor.visitMethodInsn(INVOKEVIRTUAL, wrapper, "doubleValue", "()D");
                return;
        }
    }

    /**
     * Return the get method name weaved for a value-holder attribute.
     */
    public static String getWeavedValueHolderGetMethodName(String attributeName) {
        return "_persistence_get" + attributeName + "_vh";
    }
   
    /**
     * Return the set method name weaved for a value-holder attribute.
     */
    public static String getWeavedValueHolderSetMethodName(String attributeName) {
        return "_persistence_set" + attributeName + "_vh";
    }
   
    /**
     * Return if the JAXB classes are on the classpath (if they are the XmlTransient annotation is added).
     */
    public static boolean isJAXBOnPath() {
        isJAXBOnPath = true;
        if (isJAXBOnPath == null) {
            try {
                Class.forName("javax.xml.bind.annotation.XmlTransient");
            } catch (Exception notThere) {
                isJAXBOnPath = false;
            }
        }
        return isJAXBOnPath;
    }
   
    public ClassWeaver(ClassWriter classWriter, ClassDetails classDetails) {
        super(classWriter);
        this.classDetails = classDetails;
        this.uuidGenerator = new SerialVersionUIDAdder(classWriter);
    }

    /**
     * Add a variable of type ValueHolderInterface to the class.  When this method has been run, the
     * class will contain a variable declaration similar to the following:
     * 
     * private ValueHolderInterface _persistence_variableName_vh;
     */
    public void addValueHolder(AttributeDetails attributeDetails){
        String attribute = attributeDetails.getAttributeName();
        RuntimeVisibleAnnotations annotations = null;
        // only mark @Transient if this is property access.  Otherwise, the @Transient annotation could mistakenly
        // cause the class to use attribute access.
        if (attributeDetails.getGetterMethodName() == null || attributeDetails.getGetterMethodName().equals("") || attributeDetails.weaveTransientFieldValueHolders()) {
            annotations = getTransientAnnotation();
        }
        cv.visitField(ACC_PROTECTED, "_persistence_" + attribute + "_vh", VHI_SIGNATURE, null, annotations);
    }
   
    /**
     * Add a variable of type PropertyChangeListener to the class.  When this method has been run, the class
     * will contain a variable declaration similar to the following
     *
     * private transient _persistence_listener;
     */
    public void addPropertyChangeListener(boolean attributeAccess){
        cv.visitField(ACC_PROTECTED + ACC_TRANSIENT, "_persistence_listener", PCL_SIGNATURE, null, null);
    }
   
    /**
     * Add the implementation of the changeTracker_getPropertyChangeListener method to the class.  The
     * result is a method that looks as follows:
     *
     * public PropertyChangeListener _persistence_getPropertyChangeListener() {
     *     return _persistence_listener;
     * }
     */
    public void addGetPropertyChangeListener(ClassDetails classDetails) {
        CodeVisitor cv_getPCL =  cv.visitMethod(ACC_PUBLIC, "_persistence_getPropertyChangeListener", "()" + PCL_SIGNATURE, null, null);
        cv_getPCL.visitVarInsn(ALOAD, 0);
        cv_getPCL.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_listener", PCL_SIGNATURE);
        cv_getPCL.visitInsn(ARETURN);
        cv_getPCL.visitMaxs(0, 0);
    }
       
    /**
     * Add the implementation of the changeTracker_setPropertyChangeListener method to the class.  The
     * result is a method that looks as follows:
     *
     * public void _persistence_setPropertyChangeListener(PropertyChangeListener propertychangelistener){
     *     _persistence_listener = propertychangelistener;
     * }
     */
    public void addSetPropertyChangeListener(ClassDetails classDetails){
        RuntimeVisibleAnnotations annotations = null;
        CodeVisitor cv_setPCL =  cv.visitMethod(ACC_PUBLIC, "_persistence_setPropertyChangeListener", "(" + PCL_SIGNATURE + ")V", null, annotations);
        cv_setPCL.visitVarInsn(ALOAD, 0);
        cv_setPCL.visitVarInsn(ALOAD, 1);
        cv_setPCL.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_listener", PCL_SIGNATURE);
        cv_setPCL.visitInsn(RETURN);
        cv_setPCL.visitMaxs(0, 0);
    }

    /**
     * Add a method to track property changes.  The method will look as follows:
     *
     * public void _toplink_propertyChange(String s, Object obj, Object obj1){
     *     if(_persistence_listener != null && obj != obj1){
     *         _persistence_listener.propertyChange(new PropertyChangeEvent(this, s, obj, obj1));
     *     }
     * }
     */
    public void addPropertyChange(ClassDetails classDetails){
        // create the _toplink_propertyChange() method
        CodeVisitor cv_addPC = cv.visitMethod(ACC_PUBLIC, "_persistence_propertyChange", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", null, null);
       
        // if (_toplink_Listener != null)
        cv_addPC.visitVarInsn(ALOAD, 0);
        cv_addPC.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_listener", PCL_SIGNATURE);
        Label l0 = new Label();
        cv_addPC.visitJumpInsn(IFNULL, l0);
       
        // if (obj != obj1)
        cv_addPC.visitVarInsn(ALOAD, 2);
        cv_addPC.visitVarInsn(ALOAD, 3);
        cv_addPC.visitJumpInsn(IF_ACMPEQ, l0);
       
        // _toplink_listener.propertyChange(...);
        cv_addPC.visitVarInsn(ALOAD, 0);
        cv_addPC.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_listener", PCL_SIGNATURE);
        cv_addPC.visitTypeInsn(NEW, PCE_SHORT_SIGNATURE);
        cv_addPC.visitInsn(DUP);
       
        // new PropertyChangeEvent(this, s, obj, obj1)
        cv_addPC.visitVarInsn(ALOAD, 0);
        cv_addPC.visitVarInsn(ALOAD, 1);
        cv_addPC.visitVarInsn(ALOAD, 2);
        cv_addPC.visitVarInsn(ALOAD, 3);
        cv_addPC.visitMethodInsn(INVOKESPECIAL, PCE_SHORT_SIGNATURE, "<init>", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V");
        cv_addPC.visitMethodInsn(INVOKEINTERFACE, PCL_SHORT_SIGNATURE, "propertyChange", "(" + PCE_SIGNATURE + ")V");
       
        // }
        cv_addPC.visitLabel(l0);       

        cv_addPC.visitInsn(RETURN);
        cv_addPC.visitMaxs(0, 0);
    }
   
    /**
     * Add a method that allows us to lazily initialize a valueholder we have woven in
     * This allows us to avoid initializing valueholders in the constructor.
     *
     * protected void _persistence_initialize_attribute_vh(){
     *     if(_persistence_attribute_vh == null){
     *          _persistence_attribute_vh = new ValueHolder(this.attribute); // or new ValueHolder() if property access.
     *          _persistence_attribute_vh.setIsNewlyWeavedValueHolder(true);
     *      }
     *  }
     */
    public void addInitializerForValueHolder(ClassDetails classDetails, AttributeDetails attributeDetails) {
        String attribute = attributeDetails.getAttributeName();
        String className = classDetails.getClassName();
       
        // Create a getter method for the new valueholder
        // protected void _persistence_initialize_attribute_vh(){
        CodeVisitor cv_init_VH = cv.visitMethod(ACC_PROTECTED, "_persistence_initialize_" + attribute + "_vh", "()V", null, null);

        // if(_persistence_attribute_vh == null){
        cv_init_VH.visitVarInsn(ALOAD, 0);
        cv_init_VH.visitFieldInsn(GETFIELD, className, "_persistence_" + attribute + "_vh", VHI_SIGNATURE);
        Label l0 = new Label();
        cv_init_VH.visitJumpInsn(IFNONNULL, l0);
       
        // _persistence_attribute_vh = new ValueHolder(this.attribute);
        cv_init_VH.visitVarInsn(ALOAD, 0);
        cv_init_VH.visitTypeInsn(NEW, VH_SHORT_SIGNATURE);
        cv_init_VH.visitInsn(DUP);
        if (attributeDetails.hasField()) {
            cv_init_VH.visitVarInsn(ALOAD, 0);
            cv_init_VH.visitFieldInsn(GETFIELD, className, attribute, attributeDetails.getReferenceClassType().getDescriptor());
            cv_init_VH.visitMethodInsn(INVOKESPECIAL, VH_SHORT_SIGNATURE, "<init>", "(Ljava/lang/Object;)V");
        } else {
            cv_init_VH.visitMethodInsn(INVOKESPECIAL, VH_SHORT_SIGNATURE, "<init>", "()V");
        }
        cv_init_VH.visitFieldInsn(PUTFIELD, className, "_persistence_" + attribute + "_vh", VHI_SIGNATURE);
       
        // _persistence_attribute_vh.setIsNewlyWeavedValueHolder(true);
        cv_init_VH.visitVarInsn(ALOAD, 0);
        cv_init_VH.visitFieldInsn(GETFIELD, className, "_persistence_" + attribute + "_vh", VHI_SIGNATURE);
        cv_init_VH.visitInsn(ICONST_1);
        cv_init_VH.visitMethodInsn(INVOKEINTERFACE, VHI_SHORT_SIGNATURE, "setIsNewlyWeavedValueHolder", "(Z)V");
       
        // }
        cv_init_VH.visitLabel(l0);
       
        cv_init_VH.visitInsn(RETURN);
        cv_init_VH.visitMaxs(0, 0);       
    }
   
    /**
     * Add a get method for the newly added valueholder.  Adds a method of the following form:
     *
     *  public WeavedAttributeValueHolderInterface _persistence_getfoo_vh(){
     *      _persistence_initialize_attributeName_vh();
     *      if (_persistence_vh.isCoordinatedWithProperty() || _persistence_foo_vh.isNewlyWeavedValueHolder()){
     *          EntityC object = (EntityC)getFoo();
     *          if (object != _persistence_foo_vh.getValue()){
     *              setFoo(object);
     *          }
     *      }
     *      return _persistence_foo_vh;
     *  }
     */
    public void addGetterMethodForValueHolder(ClassDetails classDetails, AttributeDetails attributeDetails){
        String attribute = attributeDetails.getAttributeName();
        String className = classDetails.getClassName();
        // Create a getter method for the new valueholder
        CodeVisitor cv_get_VH = cv.visitMethod(ACC_PUBLIC, "_persistence_get" + attribute + "_vh", "()" + VHI_SIGNATURE, null, null);
       
        // _persistence_initialize_attributeName_vh();
        cv_get_VH.visitVarInsn(ALOAD, 0);
        cv_get_VH.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), "_persistence_initialize_" + attributeDetails.getAttributeName() + "_vh", "()V");

        // if (_toplink_foo_vh.isCoordinatedWithProperty() || _toplink_foo_vh.isNewlyWeavedValueHolder()){
        cv_get_VH.visitVarInsn(ALOAD, 0);
        cv_get_VH.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_" + attribute + "_vh", VHI_SIGNATURE);
        cv_get_VH.visitMethodInsn(INVOKEINTERFACE, VHI_SHORT_SIGNATURE, "isCoordinatedWithProperty", "()Z");      
        Label l0 = new Label();
        cv_get_VH.visitJumpInsn(IFNE, l0);
        cv_get_VH.visitVarInsn(ALOAD, 0);
        cv_get_VH.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_" + attribute + "_vh", VHI_SIGNATURE);
        cv_get_VH.visitMethodInsn(INVOKEINTERFACE, VHI_SHORT_SIGNATURE, "isNewlyWeavedValueHolder", "()Z");
        Label l1 = new Label();
        cv_get_VH.visitJumpInsn(IFEQ, l1);
        cv_get_VH.visitLabel(l0);
        cv_get_VH.visitVarInsn(ALOAD, 0);   
       
        // EntityC object = (EntityC)getFoo();
        if (attributeDetails.getGetterMethodName() != null){
            cv_get_VH.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), attributeDetails.getGetterMethodName(), "()L" + attributeDetails.getReferenceClassName().replace('.','/') + ";");   
            cv_get_VH.visitTypeInsn(CHECKCAST, attributeDetails.getReferenceClassName().replace('.','/'));
        } else {
            cv_get_VH.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), "_persistence_get" + attributeDetails.attributeName, "()L" + attributeDetails.getReferenceClassName().replace('.','/') + ";");               
        }
        cv_get_VH.visitVarInsn(ASTORE, 1);

        // if (object != _toplink_foo_vh.getValue()){
        cv_get_VH.visitVarInsn(ALOAD, 1);
        cv_get_VH.visitVarInsn(ALOAD, 0);
        cv_get_VH.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_" + attribute + "_vh", VHI_SIGNATURE);
        cv_get_VH.visitMethodInsn(INVOKEINTERFACE, VHI_SHORT_SIGNATURE, "getValue", "()Ljava/lang/Object;");
        cv_get_VH.visitJumpInsn(IF_ACMPEQ, l1);
       
        // setFoo(object);
        cv_get_VH.visitVarInsn(ALOAD, 0);
        cv_get_VH.visitVarInsn(ALOAD, 1);
        if (attributeDetails.getSetterMethodName() != null){
            cv_get_VH.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), attributeDetails.getSetterMethodName(), "(L" + attributeDetails.getReferenceClassName().replace('.','/') + ";)V");
        } else {
            cv_get_VH.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), "_persistence_set" + attributeDetails.getAttributeName(), "(L" + attributeDetails.getReferenceClassName().replace('.','/') + ";)V");
        }
       
        // }
        cv_get_VH.visitLabel(l1);
       
        // return _toplink_foo_vh;
        cv_get_VH.visitVarInsn(ALOAD, 0);
        cv_get_VH.visitFieldInsn(GETFIELD, className, "_persistence_" + attribute + "_vh", VHI_SIGNATURE);
        cv_get_VH.visitInsn(ARETURN);

        cv_get_VH.visitMaxs(0, 0);
    }
   
    /**
     * Add a set method for the newly added ValueHolder.  Adds a method of this form:
     *
     *  public void _persistence_setfoo_vh(WeavedAttributeValueHolderInterface valueholderinterface){
     *      _persistence_foo_vh = valueholderinterface;
     *      if (valueholderinterface.isInstantiated()){
     *          Object object = getFoo();
     *          Object value = valueholderinterface.getValue();
     *              if (object != value){
     *                  setFoo((EntityC)value);
     *              } 
     *      }
     *  }
     */
    public void addSetterMethodForValueHolder(ClassDetails classDetails, AttributeDetails attributeDetails){
        String attribute = attributeDetails.getAttributeName();
        String className = classDetails.getClassName();
        // create a setter method for the new valueholder
        CodeVisitor cv_set_value = cv.visitMethod(ACC_PUBLIC, "_persistence_set" + attribute + "_vh", "(" + VHI_SIGNATURE + ")V", null, null);                                
       
        // _toplink_foo_vh = valueholderinterface;
        cv_set_value.visitVarInsn(ALOAD, 0);
        cv_set_value.visitVarInsn(ALOAD, 1);
        cv_set_value.visitFieldInsn(PUTFIELD, className, "_persistence_" + attribute + "_vh", VHI_SIGNATURE);   
       
        // if (valueholderinterface.isInstantiated()){
        cv_set_value.visitVarInsn(ALOAD, 1);
        cv_set_value.visitMethodInsn(INVOKEINTERFACE, VHI_SHORT_SIGNATURE, "isInstantiated", "()Z");
        Label l0 = new Label();
        cv_set_value.visitJumpInsn(IFEQ, l0);
       
        // Object object = getFoo();
        cv_set_value.visitVarInsn(ALOAD, 0);           
         if (attributeDetails.getGetterMethodName() != null){
            cv_set_value.visitMethodInsn(INVOKEVIRTUAL, className, attributeDetails.getGetterMethodName(), "()L" + attributeDetails.getReferenceClassName().replace('.','/') + ";");   
         } else {
            cv_set_value.visitMethodInsn(INVOKEVIRTUAL, className, "_persistence_get" + attributeDetails.attributeName, "()L" + attributeDetails.getReferenceClassName().replace('.','/') + ";");               
        }
        cv_set_value.visitVarInsn(ASTORE, 2);
       
        // Object value = valueholderinterface.getValue();
        cv_set_value.visitVarInsn(ALOAD, 1);
        cv_set_value.visitMethodInsn(INVOKEINTERFACE, VHI_SHORT_SIGNATURE, "getValue", "()Ljava/lang/Object;");
        cv_set_value.visitVarInsn(ASTORE, 3);      
       
        // if (object != value){
        cv_set_value.visitVarInsn(ALOAD, 2);
        cv_set_value.visitVarInsn(ALOAD, 3);
        cv_set_value.visitJumpInsn(IF_ACMPEQ, l0);
       
        // setFoo((EntityC)value);
        cv_set_value.visitVarInsn(ALOAD, 0);
        cv_set_value.visitVarInsn(ALOAD, 3);
        cv_set_value.visitTypeInsn(CHECKCAST, attributeDetails.getReferenceClassName().replace('.','/'));
         if (attributeDetails.getSetterMethodName() != null){
            cv_set_value.visitMethodInsn(INVOKEVIRTUAL, className, attributeDetails.getSetterMethodName(), "(L" + attributeDetails.getReferenceClassName().replace('.','/') + ";)V");
        } else {
            cv_set_value.visitMethodInsn(INVOKEVIRTUAL, className, "_persistence_set" + attributeDetails.getAttributeName(), "(L" + attributeDetails.getReferenceClassName().replace('.','/') + ";)V");
        }

        cv_set_value.visitLabel(l0);

        cv_set_value.visitInsn(RETURN);
        cv_set_value.visitMaxs(0, 0);
       
    }
   
    /**
     * Adds a convenience method used to replace a PUTFIELD when field access is used. The method follows
     * the following form:
     *    
     *     public void _persistence_setvariableName((VariableClas) argument) {
     *          _persistence_getvariableName();  
     *          _persistence_propertyChange("variableName", this.variableName, argument); // if change tracking enabled, wrapping primitives, i.e. new Long(item)
     *          this.variableName = argument;
     *          _persistence_variableName_vh.setValue(variableName); // if lazy enabled
     *      }
     */
    public void addSetterMethodForFieldAccess(ClassDetails classDetails, AttributeDetails attributeDetails){
        String attribute = attributeDetails.getAttributeName();
       
        // create _persistence_setvariableName
        CodeVisitor cv_set = cv.visitMethod(ACC_PUBLIC, "_persistence_set" + attribute, "(" + attributeDetails.getReferenceClassType().getDescriptor() + ")V", null, null);
       
        // Get the opcode for the load instruction.  This may be different depending on the type
        int opcode = attributeDetails.getReferenceClassType().getOpcode(Constants.ILOAD);
       
        // First call the get to ensure the attribute is instantiated correctly,
        // otherwise setting to null in change tracking will think nothing changed.
        cv_set.visitVarInsn(ALOAD, 0);
        // _persistence_getvariableName();
        cv_set.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), "_persistence_get" + attribute, "()" + attributeDetails.getReferenceClassType().getDescriptor());
       
        if (classDetails.shouldWeaveChangeTracking()) {
            // load the string attribute name as the first agument of the property change call
            cv_set.visitVarInsn(ALOAD, 0);
            cv_set.visitLdcInsn(attribute);
           
            // if the attribute is a primitive, wrap it
            // e.g. if it is an integer: new Integer(attribute)
            // This is the first part of the wrapping
            String wrapper = ClassWeaver.wrapperFor(attributeDetails.getReferenceClassType().getSort());
            if (wrapper != null){
                cv_set.visitTypeInsn(NEW, wrapper);
                cv_set.visitInsn(DUP);
            }
           
            // load the method argument
            cv_set.visitVarInsn(ALOAD, 0);
            cv_set.visitFieldInsn(GETFIELD, classDetails.getClassName(), attribute, attributeDetails.getReferenceClassType().getDescriptor());
   
            if (wrapper != null){
                // invoke the constructor for wrapping
                // e.g. new Integer(variableName)
                cv_set.visitMethodInsn(INVOKESPECIAL, wrapper, "<init>", "(" + attributeDetails.getReferenceClassType().getDescriptor() + ")V");
               
                // wrap the method argument
                // e.g. new Integer(argument)
                cv_set.visitTypeInsn(NEW, wrapper);
                cv_set.visitInsn(DUP);
                cv_set.visitVarInsn(opcode, 1);
                cv_set.visitMethodInsn(INVOKESPECIAL, wrapper, "<init>", "(" + attributeDetails.getReferenceClassType().getDescriptor() + ")V");
            } else {
                // if we are not wrapping the argument, just load it
                cv_set.visitVarInsn(ALOAD, 1);
            }
            // _persistence_propertyChange("variableName", variableName, argument);
            cv_set.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), "_persistence_propertyChange", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V");
        }
       
        // Must set variable after raising change event, so event has old and new value.
        // variableName = argument
        cv_set.visitVarInsn(ALOAD, 0);
        cv_set.visitVarInsn(opcode, 1);
        cv_set.visitFieldInsn(PUTFIELD, classDetails.getClassName(), attribute, attributeDetails.getReferenceClassType().getDescriptor());
       
        if (attributeDetails.weaveValueHolders()) {
            // _persistence_variableName_vh.setValue(argument);
            cv_set.visitVarInsn(ALOAD, 0);
            cv_set.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_" + attribute + "_vh", ClassWeaver.VHI_SIGNATURE);
            cv_set.visitVarInsn(ALOAD, 1);
            cv_set.visitMethodInsn(INVOKEINTERFACE, ClassWeaver.VHI_SHORT_SIGNATURE, "setValue", "(Ljava/lang/Object;)V");
        }
       
        cv_set.visitInsn(RETURN);
        cv_set.visitMaxs(0 ,0);
    }
   
    /**
     * Adds a convenience method used to replace a GETFIELD when field access is used. The method follows
     * the following form:
     *
     *      public (VariableClass) _persistence_getvariableName() {
     *          _persistence_checkFetched("variableName");
     *          _persistence_initialize_variableName_vh();
     *          this.variableName = ((VariableClass))_persistence_variableName_vh.getValue();
     *          return this.variableName;
     *      }
     */
    public void addGetterMethodForFieldAccess(ClassDetails classDetails, AttributeDetails attributeDetails){
        String attribute = attributeDetails.getAttributeName();

        // create the _persistenc_getvariableName method
        CodeVisitor cv_get = cv.visitMethod(ACC_PUBLIC, "_persistence_get" + attribute, "()" + attributeDetails.getReferenceClassType().getDescriptor(), null, null);
       
        if (classDetails.shouldWeaveFetchGroups()) {
            cv_get.visitVarInsn(ALOAD, 0);
            cv_get.visitLdcInsn(attribute);
            // _persistence_checkFetched("variableName");
            cv_get.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), "_persistence_checkFetched", "(Ljava/lang/String;)V");
        }
       
        if (attributeDetails.weaveValueHolders()) {
            // _persistence_initialize_variableName_vh();
            cv_get.visitVarInsn(ALOAD, 0);
            cv_get.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), "_persistence_initialize_" + attributeDetails.getAttributeName() + "_vh", "()V");
           
            // _persistenc_variableName_vh.getValue();
            cv_get.visitVarInsn(ALOAD, 0);
            cv_get.visitVarInsn(ALOAD, 0);
            cv_get.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_" + attribute + "_vh", ClassWeaver.VHI_SIGNATURE);
            cv_get.visitMethodInsn(INVOKEINTERFACE, ClassWeaver.VHI_SHORT_SIGNATURE, "getValue", "()Ljava/lang/Object;");
           
            // Add the cast: (<VariableClass>)_persistenc_variableName_vh.getValue()
            cv_get.visitTypeInsn(CHECKCAST, attributeDetails.getReferenceClassName().replace('.','/'));
           
            // add the assignment: this.variableName = (<VariableClass>)_persistenc_variableName_vh.getValue();
            cv_get.visitFieldInsn(PUTFIELD, classDetails.getClassName(), attribute, attributeDetails.getReferenceClassType().getDescriptor());
        }
       
        // return this.variableName;
        cv_get.visitVarInsn(ALOAD, 0);
        cv_get.visitFieldInsn(GETFIELD, classDetails.getClassName(), attribute, attributeDetails.getReferenceClassType().getDescriptor());
        // Get the opcode for the return insturction.  This may be different depending on the type.
        int opcode = attributeDetails.getReferenceClassType().getOpcode(Constants.IRETURN);
        cv_get.visitInsn(opcode);
        cv_get.visitMaxs(0, 0);
    }
   
    /**
     * Add a variable of type Vector, CacheKey to the class.
     * When this method has been run, the class will contain a variable declarations similar to the following:
     * 
     *  private Vector _persistence_primaryKey;
     *  private Vector _persistence_cacheKey;
     */
    public void addPersistenceEntityVariables() {
        cv.visitField(ACC_PROTECTED + ACC_TRANSIENT, "_persistence_primaryKey", VECTOR_SIGNATURE, null, null);
        cv.visitField(ACC_PROTECTED + ACC_TRANSIENT, "_persistence_cacheKey", CACHEKEY_SIGNATURE, null, null);
    }
   
    /**
     * Add an internal post clone method.
     * This will clone value holders to avoid change original/clone to effect the other.
     *
     * public Object _persistence_post_clone() {
     *     this._attribute_vh = this._attribute_vh.clone();
     *     ...
     *     this._persistence_listener = null;
     *     return this;
     * }
     */
    public void addPersistencePostClone(ClassDetails classDetails) {
        // create the _persistence_post_clone() method
        CodeVisitor cv_clone = cv.visitMethod(ACC_PUBLIC, "_persistence_post_clone", "()Ljava/lang/Object;", null, null);

        // if there is a weaved superclass, it will implement _persistence_post_clone.  Call that method
        // super._persistence_post_clone()
        if (classDetails.getSuperClassDetails() != null && classDetails.getSuperClassDetails().shouldWeaveInternal()){
           cv_clone.visitVarInsn(ALOAD, 0);
            cv_clone.visitMethodInsn(INVOKESPECIAL, classDetails.getSuperClassName(), "_persistence_post_clone", "()Ljava/lang/Object;");
        }

        if (classDetails.shouldWeaveValueHolders()) {
            for (Iterator iterator = classDetails.getAttributesMap().values().iterator(); iterator.hasNext(); ) {
                AttributeDetails attributeDetails = (AttributeDetails)iterator.next();
                if (attributeDetails.weaveValueHolders()) { // && !attributeDetails.isAttributeOnSuperClass()) {
                    // clone._attribute_vh = this._attribute_vh.clone();
                    cv_clone.visitVarInsn(ALOAD, 0);
                    cv_clone.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_" + attributeDetails.getAttributeName() + "_vh", ClassWeaver.VHI_SIGNATURE);
                    Label label = new Label();
                    cv_clone.visitJumpInsn(IFNULL, label);
                    cv_clone.visitVarInsn(ALOAD, 0);
                    cv_clone.visitVarInsn(ALOAD, 0);
                    cv_clone.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_" + attributeDetails.getAttributeName() + "_vh", ClassWeaver.VHI_SIGNATURE);
                    cv_clone.visitMethodInsn(INVOKEINTERFACE, ClassWeaver.VHI_SHORT_SIGNATURE, "clone", "()Ljava/lang/Object;");
                    cv_clone.visitTypeInsn(CHECKCAST, ClassWeaver.VHI_SHORT_SIGNATURE);
                    cv_clone.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_" + attributeDetails.getAttributeName() + "_vh", ClassWeaver.VHI_SIGNATURE);
                    cv_clone.visitLabel(label);
                }
            }
        }
        if (classDetails.shouldWeaveChangeTracking()) {
            // clone._persistence_listener = null;
            cv_clone.visitVarInsn(ALOAD, 0);
            cv_clone.visitInsn(ACONST_NULL);
            cv_clone.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_listener", PCL_SIGNATURE);
        }
        if (classDetails.shouldWeaveFetchGroups()) {
            // clone._persistence_fetchGroup = null;
            // clone._persistence_session = null;
            cv_clone.visitVarInsn(ALOAD, 0);
            cv_clone.visitInsn(ACONST_NULL);
            cv_clone.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_fetchGroup", FETCHGROUP_SIGNATURE);
            cv_clone.visitVarInsn(ALOAD, 0);
            cv_clone.visitInsn(ACONST_NULL);
            cv_clone.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_session", SESSION_SIGNATURE);
        }
       
        if (!classDetails.isEmbedable()){
          // clone._persistence_primaryKey = null;
          cv_clone.visitVarInsn(ALOAD, 0);
          cv_clone.visitInsn(ACONST_NULL);
          cv_clone.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_primaryKey", VECTOR_SIGNATURE);
          // clone._persistence_cacheKey = null;
          cv_clone.visitVarInsn(ALOAD, 0);
          cv_clone.visitInsn(ACONST_NULL);
          cv_clone.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_cacheKey", CACHEKEY_SIGNATURE);
        }
       
        // return clone;
        cv_clone.visitVarInsn(ALOAD, 0);
        cv_clone.visitInsn(ARETURN);
        cv_clone.visitMaxs(0, 0);
    }
   
    /**
     * Add an internal shallow clone method.
     * This can be used to optimize uow cloning.
     *
     * public Object _persistence_shallow_clone() {
     *     return super.clone();
     * }
     */
    public void addShallowClone(ClassDetails classDetails) {
        // create the _persistence_shallow_clone() method
        CodeVisitor cv_clone = cv.visitMethod(ACC_PUBLIC, "_persistence_shallow_clone", "()Ljava/lang/Object;", null, null);

        // return super.clone();
        cv_clone.visitVarInsn(ALOAD, 0);
        cv_clone.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "clone", "()Ljava/lang/Object;");
       
        cv_clone.visitInsn(ARETURN);
        cv_clone.visitMaxs(0, 0);
    }
   
    /**
     * Add an internal empty constructor, and new method.
     * This is used to avoid unnecessary initialization and avoid reflection.
     *
     * public void _persistence_new(PersistenceObject factory) {
     *   return new ClassType(factory);
     * }
     *
     * public ClassType(PersistenceObject factory) {
     *   super();
     * }
     */
    public void addPersistenceNew(ClassDetails classDetails) {
        // create the _persistence_new() method
        CodeVisitor cv_new = cv.visitMethod(ACC_PUBLIC, "_persistence_new", "(" + PERSISTENCE_OBJECT_SIGNATURE + ")Ljava/lang/Object;", null, null);
       
        // return new ClassType(factory);
        cv_new.visitTypeInsn(NEW, classDetails.getClassName());
        cv_new.visitInsn(DUP);
        cv_new.visitVarInsn(ALOAD, 1);
        cv_new.visitMethodInsn(INVOKESPECIAL, classDetails.getClassName(), "<init>", "(" + PERSISTENCE_OBJECT_SIGNATURE + ")V");
       
        cv_new.visitInsn(ARETURN);
        cv_new.visitMaxs(0, 0);
       
        // create the ClassType() method
        CodeVisitor cv_constructor = cv.visitMethod(ACC_PUBLIC, "<init>", "(" + PERSISTENCE_OBJECT_SIGNATURE + ")V", null, null);

        cv_constructor.visitVarInsn(ALOAD, 0);
        if (classDetails.getSuperClassDetails() == null) {
            // super();
            cv_constructor.visitMethodInsn(INVOKESPECIAL, classDetails.getSuperClassName(), "<init>", "()V");
        } else {
            // super(factory);
            cv_constructor.visitVarInsn(ALOAD, 1);
            cv_constructor.visitMethodInsn(INVOKESPECIAL, classDetails.getSuperClassName(), "<init>", "(" + PERSISTENCE_OBJECT_SIGNATURE + ")V");         
        }
        cv_constructor.visitInsn(RETURN);
        cv_constructor.visitMaxs(0, 0);
    }
   
    /**
     * Add an internal generic get and set method.
     * This is used to avoid reflection.
     *
     * public Object _persistence_get(String attribute) {
     *   if (attribute == "address") {
     *     return this.address;
     *   }
     *   if (attribute == "city") {
     *     return this.city;
     *   }
     *   return null;
     * }
     *
     * public void _persistence_set(int index, Object value) {
     *   if (attribute == "address") {
     *     this.address = (String)value;
     *   } else if (attribute == "city") {
     *     this.city = (String)city;
     *   }
     * }
     */
    public void addPersistenceGetSet(ClassDetails classDetails) {
        // create the _persistence_get() method
        CodeVisitor cv_get = cv.visitMethod(ACC_PUBLIC, "_persistence_get", "(Ljava/lang/String;)Ljava/lang/Object;", null, null);

        Label label = null;
        for (AttributeDetails attributeDetails : classDetails.getAttributesMap().values()) {
            if (!attributeDetails.isAttributeOnSuperClass()) {
                if (label != null) {
                    cv_get.visitLabel(label);
                }
                // else if (attribute == "address")
                cv_get.visitVarInsn(ALOAD, 1);
                cv_get.visitLdcInsn(attributeDetails.getAttributeName().intern());
                label = new Label();
                cv_get.visitJumpInsn(IF_ACMPNE, label);
                // return this.address
                cv_get.visitVarInsn(ALOAD, 0);
                cv_get.visitFieldInsn(GETFIELD, classDetails.getClassName(), attributeDetails.getAttributeName(), attributeDetails.getReferenceClassType().getDescriptor());
                // if this is a primitive, get the wrapper class
                String wrapper = ClassWeaver.wrapperFor(attributeDetails.getReferenceClassType().getSort());
                if (wrapper != null) {
                    // Call valueOf on the wrapper (more optimal than constructor).
                    cv_get.visitMethodInsn(INVOKESTATIC, wrapper, "valueOf", "(" + attributeDetails.getReferenceClassType().getDescriptor() + ")L" + wrapper + ";");
                }
               
                cv_get.visitInsn(ARETURN);
            }
        }
        if (label != null) {
            cv_get.visitLabel(label);
        }
        // call super, or return null
        if (classDetails.getSuperClassDetails() == null) {
            // return null;
            cv_get.visitInsn(ACONST_NULL);
        } else {
            cv_get.visitVarInsn(ALOAD, 0);
            cv_get.visitVarInsn(ALOAD, 1);
            cv_get.visitMethodInsn(INVOKESPECIAL, classDetails.getSuperClassName(), "_persistence_get", "(Ljava/lang/String;)Ljava/lang/Object;");
        }
       
        cv_get.visitInsn(ARETURN);
        cv_get.visitMaxs(0, 0);
       
        // create the _persistence_set() method
        CodeVisitor cv_set = cv.visitMethod(ACC_PUBLIC, "_persistence_set", "(Ljava/lang/String;Ljava/lang/Object;)V", null, null);

        label = null;
        for (AttributeDetails attribute : classDetails.getAttributesMap().values()) {
            if (!attribute.isAttributeOnSuperClass()) {
                if (label != null) {
                    cv_set.visitLabel(label);
                }
                // else if (attribute == "address")
                cv_set.visitVarInsn(ALOAD, 1);
                cv_set.visitLdcInsn(attribute.getAttributeName().intern());
                label = new Label();
                cv_set.visitJumpInsn(IF_ACMPNE, label);           
                // this.address = (String)value;
                cv_set.visitVarInsn(ALOAD, 0);
                cv_set.visitVarInsn(ALOAD, 2);
                String wrapper = wrapperFor(attribute.getReferenceClassType().getSort());
                if (wrapper == null) {
                    wrapper = attribute.getReferenceClassName().replace('.', '/');
                }
                cv_set.visitTypeInsn(CHECKCAST, wrapper);
                // Unwrap any primitive wrapper to its value.
                unwrapPrimitive(attribute, cv_set);
                cv_set.visitFieldInsn(PUTFIELD, classDetails.getClassName(), attribute.getAttributeName(), attribute.getReferenceClassType().getDescriptor());
                // return;
                cv_set.visitInsn(RETURN);
            }
        }
        if (label != null) {
            cv_set.visitLabel(label);
        }
        // call super, or return null
        if (classDetails.getSuperClassDetails() != null) {
            cv_set.visitVarInsn(ALOAD, 0);
            cv_set.visitVarInsn(ALOAD, 1);
            cv_set.visitVarInsn(ALOAD, 2);
            cv_set.visitMethodInsn(INVOKESPECIAL, classDetails.getSuperClassName(), "_persistence_set", "(Ljava/lang/String;Ljava/lang/Object;)V");
        }
       
        cv_set.visitInsn(RETURN);
        cv_set.visitMaxs(0, 0);
    }
   
    /**
     * Adds get/set method for PersistenceEntity interface.
     * This adds the following methods:
     *
     *  public CacheKey _persistence_getCacheKey() {
     *      return _persistence_cacheKey;
     *  }
     *  public void _persistence_setCacheKey(CacheKey key) {
     *      this._persistence_cacheKey = key;
     *  }
     *  
     *  public Vector _persistence_getPKVector() {
     *      return _persistence_primaryKey;
     *  }
     *  public void _persistence_setPKVector(Vector pk) {
     *      this._persistence_primaryKey = pk;
     *  }
     */
    public void addPersistenceEntityMethods(ClassDetails classDetails) {
        CodeVisitor cv_getCacheKey = cv.visitMethod(ACC_PUBLIC, "_persistence_getCacheKey", "()" + CACHEKEY_SIGNATURE, null, null);
        cv_getCacheKey.visitVarInsn(ALOAD, 0);
        cv_getCacheKey.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_cacheKey", CACHEKEY_SIGNATURE);
        cv_getCacheKey.visitInsn(ARETURN);
        cv_getCacheKey.visitMaxs(0, 0);
       
        CodeVisitor cv_setCacheKey = cv.visitMethod(ACC_PUBLIC, "_persistence_setCacheKey", "(" + CACHEKEY_SIGNATURE + ")V", null, null);
        cv_setCacheKey.visitVarInsn(ALOAD, 0);
        cv_setCacheKey.visitVarInsn(ALOAD, 1);
        cv_setCacheKey.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_cacheKey", CACHEKEY_SIGNATURE);
        cv_setCacheKey.visitInsn(RETURN);
        cv_setCacheKey.visitMaxs(0 ,0);
       
        CodeVisitor cv_getPKVector = cv.visitMethod(ACC_PUBLIC, "_persistence_getPKVector", "()" + VECTOR_SIGNATURE, null, null);
        cv_getPKVector.visitVarInsn(ALOAD, 0);
        cv_getPKVector.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_primaryKey", VECTOR_SIGNATURE);
        cv_getPKVector.visitInsn(ARETURN);
        cv_getPKVector.visitMaxs(0, 0);
       
        CodeVisitor cv_setPKVector = cv.visitMethod(ACC_PUBLIC, "_persistence_setPKVector", "(" + VECTOR_SIGNATURE + ")V", null, null);
        cv_setPKVector.visitVarInsn(ALOAD, 0);
        cv_setPKVector.visitVarInsn(ALOAD, 1);
        cv_setPKVector.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_primaryKey", VECTOR_SIGNATURE);
        cv_setPKVector.visitInsn(RETURN);
        cv_setPKVector.visitMaxs(0 ,0);
    }
   
    /**
     * Add a variable of type FetchGroup, Session to the class.
     * When this method has been run, the class will contain a variable declarations similar to the following:
     * 
     *  private FetchGroup _persistence_fetchGroup;
     *  private boolean _persistence_shouldRefreshFetchGroup;
     *  private Session _persistence_session;
     */
    public void addFetchGroupVariables() {
        cv.visitField(ACC_PROTECTED + ACC_TRANSIENT, "_persistence_fetchGroup", FETCHGROUP_SIGNATURE, null, null);
        cv.visitField(ACC_PROTECTED + ACC_TRANSIENT, "_persistence_shouldRefreshFetchGroup", PBOOLEAN_SIGNATURE, null, null);
        cv.visitField(ACC_PROTECTED + ACC_TRANSIENT, "_persistence_session", SESSION_SIGNATURE, null, null);
    }
   
    /**
     * Adds get/set method for FetchGroupTracker interface.
     * This adds the following methods:
     *
     *  public Session _persistence_getSession() {
     *      return _persistence_session;
     *  }
     *  public void _persistence_setSession(Session session) {
     *      this._persistence_session = session;
     *  }
     * 
     *  public FetchGroup _persistence_getFetchGroup() {
     *      return _persistence_fetchGroup;
     *  }
     *  public void _persistence_setFetchGroup(FetchGroup fetchGroup) {
     *      this._persistence_fetchGroup = fetchGroup;
     *  }
     * 
     *  public boolean _persistence_shouldRefreshFetchGroup() {
     *      return _persistence_shouldRefreshFetchGroup;
     *  }
     *  public void _persistence_setShouldRefreshFetchGroup(boolean shouldRefreshFetchGroup) {
     *      this._persistence_shouldRefreshFetchGroup = shouldRefreshFetchGroup;
     *  }
     * 
     *  public void _persistence_resetFetchGroup() {
     *  }
     *
     *  public void _persistence_isAttributeFetched(String attribute) {
     *      return this._persistence_fetchGroup == null || _persistence_fetchGroup.containsAttribute(attribute);
     *  }
     * 
     *  public void _persistence_checkFetched(String attribute) {
     *      if (!_persistence_isAttributeFetched(attribute)) {
     *          JpaHelper.loadUnfetchedObject(this);
     *      }
     *  }
     */
    public void addFetchGroupMethods(ClassDetails classDetails) {
        CodeVisitor cv_getSession = cv.visitMethod(ACC_PUBLIC, "_persistence_getSession", "()" + SESSION_SIGNATURE, null, null);
        cv_getSession.visitVarInsn(ALOAD, 0);
        cv_getSession.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_session", SESSION_SIGNATURE);
        cv_getSession.visitInsn(ARETURN);
        cv_getSession.visitMaxs(0, 0);
       
        CodeVisitor cv_setSession = cv.visitMethod(ACC_PUBLIC, "_persistence_setSession", "(" + SESSION_SIGNATURE + ")V", null, null);
        cv_setSession.visitVarInsn(ALOAD, 0);
        cv_setSession.visitVarInsn(ALOAD, 1);
        cv_setSession.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_session", SESSION_SIGNATURE);
        cv_setSession.visitInsn(RETURN);
        cv_setSession.visitMaxs(0 ,0);
       
        CodeVisitor cv_getFetchGroup = cv.visitMethod(ACC_PUBLIC, "_persistence_getFetchGroup", "()" + FETCHGROUP_SIGNATURE, null, null);
        cv_getFetchGroup.visitVarInsn(ALOAD, 0);
        cv_getFetchGroup.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_fetchGroup", FETCHGROUP_SIGNATURE);
        cv_getFetchGroup.visitInsn(ARETURN);
        cv_getFetchGroup.visitMaxs(0, 0);
       
        CodeVisitor cv_setFetchGroup = cv.visitMethod(ACC_PUBLIC, "_persistence_setFetchGroup", "(" + FETCHGROUP_SIGNATURE + ")V", null, null);
        cv_setFetchGroup.visitVarInsn(ALOAD, 0);
        cv_setFetchGroup.visitVarInsn(ALOAD, 1);
        cv_setFetchGroup.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_fetchGroup", FETCHGROUP_SIGNATURE);
        cv_setFetchGroup.visitInsn(RETURN);
        cv_setFetchGroup.visitMaxs(0 ,0);
       
        CodeVisitor cv_shouldRefreshFetchGroup = cv.visitMethod(ACC_PUBLIC, "_persistence_shouldRefreshFetchGroup", "()" + PBOOLEAN_SIGNATURE, null, null);
        cv_shouldRefreshFetchGroup.visitVarInsn(ALOAD, 0);
        cv_shouldRefreshFetchGroup.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_shouldRefreshFetchGroup", PBOOLEAN_SIGNATURE);
        cv_shouldRefreshFetchGroup.visitInsn(IRETURN);
        cv_shouldRefreshFetchGroup.visitMaxs(0, 0);
       
        CodeVisitor cv_setShouldRefreshFetchGroup = cv.visitMethod(ACC_PUBLIC, "_persistence_setShouldRefreshFetchGroup", "(" + PBOOLEAN_SIGNATURE + ")V", null, null);
        cv_setShouldRefreshFetchGroup.visitVarInsn(ALOAD, 0);
        cv_setShouldRefreshFetchGroup.visitVarInsn(ILOAD, 1);
        cv_setShouldRefreshFetchGroup.visitFieldInsn(PUTFIELD, classDetails.getClassName(), "_persistence_shouldRefreshFetchGroup", PBOOLEAN_SIGNATURE);
        cv_setShouldRefreshFetchGroup.visitInsn(RETURN);
        cv_setShouldRefreshFetchGroup.visitMaxs(0 ,0);
       
        CodeVisitor cv_resetFetchGroup = cv.visitMethod(ACC_PUBLIC, "_persistence_resetFetchGroup", "()V", null, null);
        cv_resetFetchGroup.visitInsn(RETURN);
        cv_resetFetchGroup.visitMaxs(0, 0);
       
        CodeVisitor cv_isAttributeFetched = cv.visitMethod(ACC_PUBLIC, "_persistence_isAttributeFetched", "(Ljava/lang/String;)Z", null, null);
        cv_isAttributeFetched.visitVarInsn(ALOAD, 0);
        cv_isAttributeFetched.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_fetchGroup", FETCHGROUP_SIGNATURE);
        Label gotoTrue = new Label();
        cv_isAttributeFetched.visitJumpInsn(IFNULL, gotoTrue);
        cv_isAttributeFetched.visitVarInsn(ALOAD, 0);
        cv_isAttributeFetched.visitFieldInsn(GETFIELD, classDetails.getClassName(), "_persistence_fetchGroup", FETCHGROUP_SIGNATURE);
        cv_isAttributeFetched.visitVarInsn(ALOAD, 1);
        cv_isAttributeFetched.visitMethodInsn(INVOKEVIRTUAL, FETCHGROUP_SHORT_SIGNATURE, "containsAttribute", "(Ljava/lang/String;)Z");
        Label gotoFalse = new Label();
        cv_isAttributeFetched.visitJumpInsn(IFEQ, gotoFalse);
        cv_isAttributeFetched.visitLabel(gotoTrue);
        cv_isAttributeFetched.visitInsn(ICONST_1);
        Label gotoReturn = new Label();
        cv_isAttributeFetched.visitJumpInsn(GOTO, gotoReturn);
        cv_isAttributeFetched.visitLabel(gotoFalse);
        cv_isAttributeFetched.visitInsn(ICONST_0);
        cv_isAttributeFetched.visitLabel(gotoReturn);
        cv_isAttributeFetched.visitInsn(IRETURN);
        cv_isAttributeFetched.visitMaxs(0 ,0);
       
        CodeVisitor cv_checkFetched = cv.visitMethod(ACC_PUBLIC, "_persistence_checkFetched", "(Ljava/lang/String;)V", null, null);
        cv_checkFetched.visitVarInsn(ALOAD, 0);
        cv_checkFetched.visitVarInsn(ALOAD, 1);
        cv_checkFetched.visitMethodInsn(INVOKEVIRTUAL, classDetails.getClassName(), "_persistence_isAttributeFetched", "(Ljava/lang/String;)Z");
        gotoReturn = new Label();
        cv_checkFetched.visitJumpInsn(IFNE, gotoReturn);
        cv_checkFetched.visitVarInsn(ALOAD, 0);
        cv_checkFetched.visitMethodInsn(INVOKESTATIC, "org/eclipse/persistence/jpa/JpaHelper", "loadUnfetchedObject", "(Ljava/lang/Object;)V");
        cv_checkFetched.visitLabel(gotoReturn);
        cv_checkFetched.visitInsn(RETURN);
        cv_checkFetched.visitMaxs(0 ,0);
    }

    /**
     * Return the JPA transient annotation for weaving.
     */
    protected RuntimeVisibleAnnotations getTransientAnnotation(){
        RuntimeVisibleAnnotations attrs = new RuntimeVisibleAnnotations();
        //Annotation transientAnnotation = new Annotation("Ljavax/persistence/Transient;");
        Annotation transientAnnotation = new Annotation(Type.getDescriptor(javax.persistence.Transient.class));
        if (isJAXBOnPath()) {
            try {
                attrs.annotations.add(new Annotation(Type.getDescriptor(Class.forName("javax.xml.bind.annotation.XmlTransient"))));
            } catch (Exception exception) {}
        }
        attrs.annotations.add(transientAnnotation);
        return attrs;
    }
   
    /**
     * Visit the class byte-codes and modify to weave Persistence interfaces.
     * This add PersistenceWeaved, PersistenceWeavedLazy, PersistenceWeavedChangeTracking, PersistenceEntity, ChangeTracker.
     * The new interfaces are pass to the super weaver.
     */
    public void visit(int version, int access, String name, String superName, String[] interfaces, String sourceFile) {
        this.uuidGenerator.visit(version, access, name, superName, interfaces, sourceFile);
        boolean weaveCloneable = true;
        // To prevent 'double' weaving: scan for PersistenceWeaved interface.
        for (int index = 0; index < interfaces.length; index++) {
            String existingInterface = interfaces[index];
            if (PERSISTENCE_WEAVED_SHORT_SIGNATURE.equals(existingInterface)) {
                this.alreadyWeaved = true;
                super.visit(version, access, name, superName, interfaces, sourceFile);
                return;
            } else if (CT_SHORT_SIGNATURE.equals(existingInterface)) {
                // Disable weaving of change tracking if already implemented (such as by user).
                classDetails.setShouldWeaveChangeTracking(false);
            } else if (CLONEABLE_SHORT_SIGNATURE.equals(existingInterface)) {
                weaveCloneable = false;
            }
        }
        int newInterfacesLength = interfaces.length;
        // Cloneable
        int cloneableIndex = 0;
        weaveCloneable = classDetails.shouldWeaveInternal() && weaveCloneable && (classDetails.getSuperClassDetails() == null);
        if (weaveCloneable) {
            cloneableIndex = newInterfacesLength;
            newInterfacesLength++;
        }
        // PersistenceWeaved
        int persistenceWeavedIndex = newInterfacesLength;
        newInterfacesLength++;
        // PersistenceEntity
        int persistenceEntityIndex = 0;
        boolean persistenceEntity = classDetails.shouldWeaveInternal() && (classDetails.getSuperClassDetails() == null) && (!classDetails.isEmbedable());
        if (persistenceEntity) {
            persistenceEntityIndex = newInterfacesLength;
            newInterfacesLength++;
        }
        // PersistenceObject
        int persistenceObjectIndex = 0;
        boolean persistenceObject = classDetails.shouldWeaveInternal();
        if (persistenceObject) {
            persistenceObjectIndex = newInterfacesLength;
            newInterfacesLength++;
        }
        // FetchGroupTracker
        int fetchGroupTrackerIndex = 0;
        boolean fetchGroupTracker = classDetails.shouldWeaveFetchGroups() && (classDetails.getSuperClassDetails() == null);
        if (fetchGroupTracker) {
            fetchGroupTrackerIndex = newInterfacesLength;
            newInterfacesLength++;
        }
        int persistenceWeavedFetchGroupsIndex = 0;
        if (classDetails.shouldWeaveFetchGroups()) {
            persistenceWeavedFetchGroupsIndex = newInterfacesLength;
            newInterfacesLength++;
        }
        // PersistenceWeavedLazy
        int persistenceWeavedLazyIndex = 0;
        if (classDetails.shouldWeaveValueHolders()) {
            persistenceWeavedLazyIndex = newInterfacesLength;
            newInterfacesLength++;
        }
        // ChangeTracker
        boolean changeTracker = !classDetails.doesSuperclassWeaveChangeTracking() && classDetails.shouldWeaveChangeTracking();
        int persistenceWeavedChangeTrackingIndex = 0;
        int changeTrackerIndex = 0;
        if (changeTracker) {
            changeTrackerIndex = newInterfacesLength;
            newInterfacesLength++;
        }
        if (classDetails.shouldWeaveChangeTracking()) {
            persistenceWeavedChangeTrackingIndex = newInterfacesLength;
            newInterfacesLength++;
        }
       
        String[] newInterfaces = new String[newInterfacesLength];
        System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
        // Add 'marker' org.eclipse.persistence.internal.weaving.PersistenceWeaved interface.
        newInterfaces[persistenceWeavedIndex] = PERSISTENCE_WEAVED_SHORT_SIGNATURE;           
        weaved = true;
        // Add Cloneable interface.
        if (weaveCloneable) {
            newInterfaces[cloneableIndex] = CLONEABLE_SHORT_SIGNATURE;
        }
        // Add org.eclipse.persistence.internal.descriptors.PersistenceEntity interface.
        if (persistenceEntity) {
            newInterfaces[persistenceEntityIndex] = PERSISTENCE_ENTITY_SHORT_SIGNATURE;
        }
        // Add org.eclipse.persistence.internal.descriptors.PersistenceObject interface.
        if (persistenceObject) {
            newInterfaces[persistenceObjectIndex] = PERSISTENCE_OBJECT_SHORT_SIGNATURE;
        }
        // Add org.eclipse.persistence.queries.FetchGroupTracker interface.
        if (fetchGroupTracker) {
            newInterfaces[fetchGroupTrackerIndex] = FETCHGROUP_TRACKER_SHORT_SIGNATURE;
        }
        if (classDetails.shouldWeaveFetchGroups()) {
            newInterfaces[persistenceWeavedFetchGroupsIndex] = WEAVED_FETCHGROUPS_SHORT_SIGNATURE;
        }
        // Add marker interface for LAZY.
        if (classDetails.shouldWeaveValueHolders()) {
            newInterfaces[persistenceWeavedLazyIndex] = TW_LAZY_SHORT_SIGNATURE;
        }
        // Add marker interface and change tracker interface for change tracking.
        if (changeTracker) {
            newInterfaces[changeTrackerIndex] = CT_SHORT_SIGNATURE;
        }
        if (classDetails.shouldWeaveChangeTracking()) {
            newInterfaces[persistenceWeavedChangeTrackingIndex] = TW_CT_SHORT_SIGNATURE;
        }
        super.visit(version, access, name, superName, newInterfaces, sourceFile);
    }
   
    public void visitField (int access, String name, String desc, Object value, Attribute attrs) {
        this.uuidGenerator.visitField(access, name, desc, value, attrs);       
        super.visitField(access, name, desc, value, attrs);
    }
 
    /**
     * Construct a MethodWeaver and allow it to process the method.
     */
    public CodeVisitor visitMethod(int access, String methodName, String desc, String[] exceptions, Attribute attrs) {
        this.uuidGenerator.visitMethod(access, methodName, desc, exceptions, attrs);
        if (!alreadyWeaved) {
            // skip constructors, they will not changed
            if ("<init>".equals(methodName)||"<cinit>".equals(methodName)) {
                return super.visitMethod(access, methodName, desc, exceptions, attrs);
            } else {
                // remaining modifications to the 'body' of the class are delegated to MethodWeaver
                return new MethodWeaver(this, methodName, desc, cv.visitMethod(access, methodName, desc, exceptions, attrs));               
            }
        } else {
            return super.visitMethod(access, methodName, desc, exceptions, attrs);
        }
    }   

    public void visitAttribute(Attribute attr) {
        if (!alreadyWeaved) {
            cv.visitAttribute(attr);
        } else {
            super.visitAttribute(attr);
        }
    }

    /**
     * Visit the end of the class byte codes.
     * Add any new methods or variables to the end.
     */
    public void visitEnd() {
        if (alreadyWeaved) {
            return;
        }
       
        if (this.classDetails.shouldWeaveInternal()) {
            // Add a serial UID if one was not defined in the class to allow portable serialization.
            if (!this.uuidGenerator.hasSVUID()) {
                long suid = this.uuidGenerator.computeSVUID();
                this.cv.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, "serialVersionUID", LONG_SIGNATURE, suid, null);
            }
            // Add a persistence and shallow clone method.
            addPersistencePostClone(this.classDetails);
            if (this.classDetails.getSuperClassDetails() == null) {
                addShallowClone(this.classDetails);       
                if (!this.classDetails.isEmbedable()) {
                    // Add PersistenceEntity variables and methods.
                    addPersistenceEntityVariables();
                    addPersistenceEntityMethods(this.classDetails);
                    this.weavedPersistenceEntity = true;
                }
            }
            // Add empty new method and generic get/set methods.
            addPersistenceNew(this.classDetails);
            addPersistenceGetSet(this.classDetails);
        }
       
        boolean attributeAccess = false;
        // For each attribute we need to check what methods and variables to add.
        for (Iterator iterator = this.classDetails.getAttributesMap().values().iterator(); iterator.hasNext();) {
            AttributeDetails attributeDetails = (AttributeDetails)iterator.next();
            // Only add to classes that actually contain the attribute we are processing
            // an attribute could be in the classDetails but not actually in the class
            // if it is owned by a MappedSuperClass.
            if (!attributeDetails.isAttributeOnSuperClass()) {
                if (attributeDetails.weaveValueHolders()) {
                    // We will add valueholders and methods to classes that have not already been weaved
                    // and classes that actually contain the attribute we are processing
                    // an attribute could be in the classDetails but not actually in the class
                    // if it is owned by a MappedSuperClass.
                    if (!attributeDetails.isAttributeOnSuperClass()) {
                        weaved = true;
                        weavedLazy = true;
                        addValueHolder(attributeDetails);
                        addInitializerForValueHolder(classDetails, attributeDetails);
                        addGetterMethodForValueHolder(classDetails, attributeDetails);
                        addSetterMethodForValueHolder(classDetails, attributeDetails);                       
                    }
                }
                if (classDetails.shouldWeaveChangeTracking() || classDetails.shouldWeaveFetchGroups() || attributeDetails.weaveValueHolders()) {
                    if (attributeDetails.hasField()) {
                        weaved = true;
                        addGetterMethodForFieldAccess(classDetails, attributeDetails);
                        addSetterMethodForFieldAccess(classDetails, attributeDetails);
                        attributeAccess = true;
                    }
                }
            }
        }
        if (classDetails.shouldWeaveChangeTracking()) {
            weaved = true;
            weavedChangeTracker = true;
            if ((classDetails.getSuperClassDetails() == null) || (!classDetails.doesSuperclassWeaveChangeTracking())) {
                addPropertyChangeListener(attributeAccess);
                addGetPropertyChangeListener(classDetails);
                addSetPropertyChangeListener(classDetails);
                addPropertyChange(classDetails);
            }
        }
        if (classDetails.shouldWeaveFetchGroups()) {
            weaved = true;
            weavedFetchGroups = true;
            if (classDetails.getSuperClassDetails() == null) {         
                addFetchGroupVariables();
                addFetchGroupMethods(this.classDetails);
            }
        }
        super.visitEnd();
    }
}
TOP

Related Classes of org.eclipse.persistence.internal.weaving.ClassWeaver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.