/*******************************************************************************
* 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();
}
}