Package org.jpox.enhancer.bcel

Source Code of org.jpox.enhancer.bcel.BCELClassEnhancer

/**********************************************************************
Copyright (c) 2004 Kikuchi Kousuke and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
2004 Andy Jefferson - localised, and javadocs
2004 Andy Jefferson - changed logged/output messages from metadata read
2004 Andy Jefferson - added JDOConfigManager to load superclasses etc
2005 Erik Bengtson - support pc aware classes and fix access to getfield
and setfield
2006 Andy Jefferson - changed to be BCEL variant of ClassEnhancer
...
**********************************************************************/
package org.jpox.enhancer.bcel;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Comparator;

import javax.jdo.JDOFatalException;
import javax.jdo.spi.PersistenceCapable;

import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantFieldref;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.FieldOrMethod;
import org.apache.bcel.classfile.InnerClasses;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.CPInstruction;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldGen;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.GETFIELD;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InstructionTargeter;
import org.apache.bcel.generic.LDC;
import org.apache.bcel.generic.LDC2_W;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUTFIELD;
import org.apache.bcel.generic.TargetLostException;
import org.apache.bcel.generic.Type;
import org.apache.bcel.verifier.VerificationResult;
import org.apache.bcel.verifier.Verifier;
import org.apache.bcel.verifier.VerifierFactory;
import org.apache.bcel.verifier.VerifierFactoryObserver;
import org.jpox.ClassLoaderResolver;
import org.jpox.enhancer.AbstractClassEnhancer;
import org.jpox.enhancer.ClassEnhancer;
import org.jpox.enhancer.ClassField;
import org.jpox.enhancer.ClassMethod;
import org.jpox.enhancer.bcel.metadata.BCELClassMetaData;
import org.jpox.enhancer.bcel.metadata.BCELFieldPropertyMetaData;
import org.jpox.enhancer.bcel.metadata.BCELMember;
import org.jpox.enhancer.bcel.method.CheckReadMethod;
import org.jpox.enhancer.bcel.method.CheckWriteMethod;
import org.jpox.enhancer.bcel.method.DefaultConstructor;
import org.jpox.enhancer.bcel.method.InitFieldFlags;
import org.jpox.enhancer.bcel.method.InitFieldNames;
import org.jpox.enhancer.bcel.method.InitFieldTypes;
import org.jpox.enhancer.bcel.method.InitPersistenceCapableSuperClass;
import org.jpox.enhancer.bcel.method.JdoCopyField;
import org.jpox.enhancer.bcel.method.JdoCopyFields;
import org.jpox.enhancer.bcel.method.JdoCopyKeyFieldsFromObjectId;
import org.jpox.enhancer.bcel.method.JdoCopyKeyFieldsFromObjectId2;
import org.jpox.enhancer.bcel.method.JdoCopyKeyFieldsToObjectId;
import org.jpox.enhancer.bcel.method.JdoCopyKeyFieldsToObjectId2;
import org.jpox.enhancer.bcel.method.JdoGetManagedFieldCount;
import org.jpox.enhancer.bcel.method.JdoGetObjectId;
import org.jpox.enhancer.bcel.method.JdoGetPersistenceManager;
import org.jpox.enhancer.bcel.method.JdoGetTransactionalObjectId;
import org.jpox.enhancer.bcel.method.JdoGetVersion;
import org.jpox.enhancer.bcel.method.JdoIsDeleted;
import org.jpox.enhancer.bcel.method.JdoIsDetached;
import org.jpox.enhancer.bcel.method.JdoIsDirty;
import org.jpox.enhancer.bcel.method.JdoIsNew;
import org.jpox.enhancer.bcel.method.JdoIsPersistent;
import org.jpox.enhancer.bcel.method.JdoIsTransactional;
import org.jpox.enhancer.bcel.method.JdoMakeDirty;
import org.jpox.enhancer.bcel.method.JdoNewInstance1;
import org.jpox.enhancer.bcel.method.JdoNewInstance2;
import org.jpox.enhancer.bcel.method.JdoNewObjectIdInstance1;
import org.jpox.enhancer.bcel.method.JdoNewObjectIdInstance2;
import org.jpox.enhancer.bcel.method.JdoPreSerialize;
import org.jpox.enhancer.bcel.method.JdoProvideField;
import org.jpox.enhancer.bcel.method.JdoProvideFields;
import org.jpox.enhancer.bcel.method.JdoReplaceDetachedState;
import org.jpox.enhancer.bcel.method.JdoReplaceField;
import org.jpox.enhancer.bcel.method.JdoReplaceFields;
import org.jpox.enhancer.bcel.method.JdoReplaceFlags;
import org.jpox.enhancer.bcel.method.JdoReplaceStateManager;
import org.jpox.enhancer.bcel.method.LoadClass;
import org.jpox.enhancer.bcel.method.MediateReadMethod;
import org.jpox.enhancer.bcel.method.MediateWriteMethod;
import org.jpox.enhancer.bcel.method.NormalGetMethod;
import org.jpox.enhancer.bcel.method.NormalSetMethod;
import org.jpox.enhancer.bcel.method.ParentManagedFieldNum;
import org.jpox.enhancer.bcel.method.PropertyGetterMethod;
import org.jpox.enhancer.bcel.method.PropertySetterMethod;
import org.jpox.enhancer.bcel.method.SuperClone;
import org.jpox.enhancer.bcel.method.WriteObject;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ClassMetaData;
import org.jpox.metadata.ClassPersistenceModifier;
import org.jpox.metadata.FieldPersistenceModifier;
import org.jpox.util.ClassUtils;
import org.jpox.util.JPOXLogger;
import org.jpox.util.StringUtils;

/**
* Class enhancer using Apache BCEL (http://jakarta.apache.org/bcel).
* @version $Revision: 1.44 $
*/
public class BCELClassEnhancer extends AbstractClassEnhancer
{
    /** Original class */
    public final JavaClass oldClass;

    /** enhancing class */
    public final ClassGen newClass;

    /** constant pool of enhancing class */
    public final ConstantPoolGen constantPoolGen;

    /** class type of enhancing class */
    public final ObjectType classType;

    /** serialVersionUID value */
    protected long addSerialVersionUID;

    /** Field type of jdoFlag */
    public final static Type OT_Flag = Type.BYTE;

    /** Type of Object[] */
    public final static Type OT_ObjectArray = new ArrayType(Type.OBJECT, 1);

    /** Object type of SingleFieldIdentity classes */
    public final static ObjectType OT_LongIdentity = new ObjectType(CN_LongIdentity);

    public final static ObjectType OT_StringIdentity = new ObjectType(CN_StringIdentity);

    public final static ObjectType OT_ShortIdentity = new ObjectType(CN_ShortIdentity);

    public final static ObjectType OT_IntIdentity = new ObjectType(CN_IntIdentity);

    public final static ObjectType OT_CharIdentity = new ObjectType(CN_CharIdentity);

    public final static ObjectType OT_ByteIdentity = new ObjectType(CN_ByteIdentity);

    public final static ObjectType OT_ObjectIdentity = new ObjectType(CN_ObjectIdentity);

    /** Object type of javax.spi.PersistenceManager */
    public final static ObjectType OT_PersistenceManager = new ObjectType(CN_PersistenceManager);

    /** Object type of javax.jdo.spi.PersistenceCapable.ObjectIdFieldConsumer */
    public final static ObjectType OT_ObjectIdFieldConsumer = new ObjectType(CN_ObjectIdFieldConsumer);

    /** Object type of javax.jdo.spi.PersistenceCapable.ObjectIdFieldSupplier */
    public final static ObjectType OT_ObjectIdFieldSupplier = new ObjectType(CN_ObjectIdFieldSupplier);

    /** Object type of java.util.BitSet */
    public final static ObjectType OT_BitSet = new ObjectType(CN_BitSet);

    /** Object type of javax.jdo.spi.StateManager */
    public final static ObjectType OT_StateManager = new ObjectType(CN_StateManager);

    /** Object type of javax.jdo.spi.PersistenceCapable */
    public final static ObjectType OT_PersistenceCapable = new ObjectType(CN_PersistenceCapable);

    /** Object type of javax.jdo.spi.Detachable */
    public final static ObjectType OT_Detachable = new ObjectType(CN_Detachable);

    /** Object type of javax.jdo.spi.JDOImplHelper */
    public final static ObjectType OT_JDOImplHelper = new ObjectType(CN_JDOImplHelper);

    /** Object type of java.lang.Class */
    public final static ObjectType OT_CLASS = new ObjectType(CN_Class);

    /**
     * Constructor.
     * @param cmd MetaData for the class to be enhanced
     * @param clr ClassLoader resolver
     */
    public BCELClassEnhancer(ClassMetaData cmd, ClassLoaderResolver clr, byte[] classBytes)
    {
        this(cmd,clr);
        JPOXRepository rep = (JPOXRepository)Repository.getRepository();
        rep.defineClass(cmd.getFullClassName(), classBytes);
    }
   
    /**
     * Constructor.
     * @param cmd MetaData for the class to be enhanced
     * @param clr ClassLoader resolver
     */
    public BCELClassEnhancer(ClassMetaData cmd, ClassLoaderResolver clr)
    {
        super(cmd, clr);

        // Make sure we have a JPOXRepository
        org.apache.bcel.util.Repository rep = Repository.getRepository();
        if (rep == null || !(rep instanceof JPOXRepository))
        {
            Repository.setRepository(new JPOXRepository(clr));
        }

        if (!(cmd instanceof BCELClassMetaData))
        {
            throw new RuntimeException("MetaData for class " + cmd.getFullClassName() + " is not BCEL-specific and so cannot be used");
        }
        if (JPOXLogger.ENHANCER.isDebugEnabled())
        {
            JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.SetupClass", className));
        }

        // newClass.replaceMethod();

        this.oldClass = ((BCELClassMetaData) cmd).getEnhanceClass();
        this.newClass = ((BCELClassMetaData) cmd).getClassGen();
        this.constantPoolGen = this.newClass.getConstantPool();
        this.classType = new ObjectType(className);
        for (int i = 0; i < cmd.getNoOfMembers(); i++)
        {
            AbstractMemberMetaData fmd = (AbstractMemberMetaData) cmd.getMetaDataForMemberAtRelativePosition(i);
            if (!fmd.isFinal() && !fmd.isStatic())
            {
                String fieldName = fmd.getName();
                boolean found = false;
                if( fmd.isProperty() )
                {
                    Method method = BCELUtils.getGetterByName(fieldName, newClass);
                    if (method != null)
                    {
                        found = true;
                    }
                }
                if (!found)
                {
                    Field field = BCELUtils.getFieldByName(fieldName, newClass);
                    if (field == null)
                    {
                        if (!fmd.isProperty())
                        {
                            throw new RuntimeException(LOCALISER.msg("Enhancer.ClassHasNoSuchField", newClass.getClassName(), fieldName));
                        }
                        else
                        {
                            throw new RuntimeException(LOCALISER.msg("Enhancer.ClassHasNoSuchMethod", newClass.getClassName(), ClassUtils.getJavaBeanGetterName(fieldName,fmd.getType()==Boolean.class)));
                        }
                    }
                }
            }
        }
    }

    /**
     * Convenience accessor for the class name that is stored in a particular
     * class.
     * @param filename Name of the file
     * @return The class name
     */
    public static String getClassNameForFileName(String filename)
    {
        try
        {
            // Use BCEL ClassParser to find the class name
            return new ClassParser(filename).parse().getClassName();
        }
        catch (IOException ioe)
        {
            return null;
        }
    }

    // ---------------------------- Initialisation ----------------------------------

    /**
     * Initialise the methods that we need to add to this class
     */
    protected void initialiseMethodsList()
    {
        if (cmd.getPersistenceCapableSuperclass() == null)
        {
            // Root persistence class methods
            methodsToAdd.add(JdoCopyKeyFieldsFromObjectId.getInstance(this));
            methodsToAdd.add(JdoCopyKeyFieldsFromObjectId2.getInstance(this));
            methodsToAdd.add(JdoCopyKeyFieldsToObjectId.getInstance(this));
            methodsToAdd.add(JdoCopyKeyFieldsToObjectId2.getInstance(this));
            methodsToAdd.add(JdoGetObjectId.getInstance(this));
            methodsToAdd.add(JdoGetVersion.getInstance(this));
            methodsToAdd.add(JdoPreSerialize.getInstance(this));
            methodsToAdd.add(JdoGetPersistenceManager.getInstance(this));
            methodsToAdd.add(JdoGetTransactionalObjectId.getInstance(this));
            methodsToAdd.add(JdoIsDeleted.getInstance(this));
            methodsToAdd.add(JdoIsDirty.getInstance(this));
            methodsToAdd.add(JdoIsNew.getInstance(this));
            methodsToAdd.add(JdoIsPersistent.getInstance(this));
            methodsToAdd.add(JdoIsTransactional.getInstance(this));
            methodsToAdd.add(JdoMakeDirty.getInstance(this));
            methodsToAdd.add(JdoNewObjectIdInstance1.getInstance(this));
            methodsToAdd.add(JdoNewObjectIdInstance2.getInstance(this));
            methodsToAdd.add(JdoProvideFields.getInstance(this));
            methodsToAdd.add(JdoReplaceFields.getInstance(this));
            methodsToAdd.add(JdoReplaceFlags.getInstance(this));
            methodsToAdd.add(JdoReplaceStateManager.getInstance(this));
        }

        if (requiresDetachable())
        {
            // Detachable methods
            methodsToAdd.add(JdoReplaceDetachedState.getInstance(this));
        }

        methodsToAdd.add(JdoIsDetached.getInstance(this));
        methodsToAdd.add(JdoNewInstance1.getInstance(this));
        methodsToAdd.add(JdoNewInstance2.getInstance(this));
        methodsToAdd.add(JdoReplaceField.getInstance(this));
        methodsToAdd.add(JdoProvideField.getInstance(this));
        methodsToAdd.add(JdoCopyField.getInstance(this));
        methodsToAdd.add(JdoCopyFields.getInstance(this));
        methodsToAdd.add(InitFieldNames.getInstance(this));
        methodsToAdd.add(InitFieldTypes.getInstance(this));
        methodsToAdd.add(InitFieldFlags.getInstance(this));
        methodsToAdd.add(ParentManagedFieldNum.getInstance(this));
        methodsToAdd.add(JdoGetManagedFieldCount.getInstance(this));
        methodsToAdd.add(InitPersistenceCapableSuperClass.getInstance(this));
        methodsToAdd.add(LoadClass.getInstance(this));
        methodsToAdd.add(SuperClone.getInstance(this));

        if (checkHasDefaultConstructor() != null)
        {
            // Add default constructor where a constructor exists but not a
            // default one.
            methodsToAdd.add(DefaultConstructor.getInstance(this));
        }

        try
        {
            // Add writeObject() if Serializable and not specified
            if (BCELUtils.isInstanceof(oldClass, Serializable.class) &&
                BCELUtils.findMethod(newClass, "writeObject", "(Ljava/io/ObjectOutputStream;)V") == null)
            {
                methodsToAdd.add(WriteObject.getInstance(this));
            }
        }
        catch (ClassNotFoundException e)
        {
            JPOXLogger.ENHANCER.error(LOCALISER.msg("Enhancer.ErrorEnhancingClass", cmd.getFullClassName(), e));
        }
    }

    /**
     * Initialise the fields that we need to add to this class
     */
    protected void initialiseFieldsList()
    {
        if (cmd.getPersistenceCapableSuperclass() == null)
        {
            // Root persistent class fields
            fieldsToAdd.add(new ClassField(this, FN_StateManager, Constants.ACC_PROTECTED | Constants.ACC_TRANSIENT, OT_StateManager));
            fieldsToAdd.add(new ClassField(this, FN_Flag, Constants.ACC_PROTECTED | Constants.ACC_TRANSIENT, OT_Flag));
        }

        if (requiresDetachable())
        {
            // Detachable fields
            fieldsToAdd.add(new ClassField(this, FN_JdoDetachedState, Constants.ACC_PROTECTED, OT_ObjectArray));
        }

        fieldsToAdd.add(new ClassField(this, FN_FieldFlags, Constants.ACC_PRIVATE | Constants.ACC_STATIC | Constants.ACC_FINAL,
                new ArrayType(Type.BYTE, 1)));
        fieldsToAdd.add(new ClassField(this, FN_PersistenceCapableSuperclass,
                Constants.ACC_PRIVATE | Constants.ACC_STATIC | Constants.ACC_FINAL, OT_CLASS));
        fieldsToAdd.add(new ClassField(this, FN_FieldTypes, Constants.ACC_PRIVATE | Constants.ACC_STATIC | Constants.ACC_FINAL,
                new ArrayType(Class.class.getName(), 1)));
        fieldsToAdd.add(new ClassField(this, FN_FieldNames, Constants.ACC_PRIVATE | Constants.ACC_STATIC | Constants.ACC_FINAL,
                new ArrayType(Type.STRING, 1)));
        fieldsToAdd.add(new ClassField(this, FN_JdoInheritedFieldCount, Constants.ACC_PRIVATE | Constants.ACC_STATIC | Constants.ACC_FINAL,
                Type.INT));

        try
        {
            // serialVersionUID if not specified
            if (BCELUtils.isInstanceof(oldClass, Serializable.class) &&
                BCELUtils.getFieldByName(FN_serialVersionUID, newClass) == null)
            {
                fieldsToAdd.add(new ClassField(this, FN_serialVersionUID,
                    Constants.ACC_PRIVATE | Constants.ACC_STATIC | Constants.ACC_FINAL, Type.LONG));
                addSerialVersionUID = new SerialVersionUID().computeSerialVersionUID(oldClass);
            }
        }
        catch (ClassNotFoundException e)
        {
            JPOXLogger.ENHANCER.error(LOCALISER.msg("Enhancer.ErrorEnhancingClass", cmd.getFullClassName(), e));
        }
    }

    // ---------------------------------- Checking Methods -----------------------------------

    /**
     * Method to verify the class.
     * @throws Exception Thrown if a verification error occurs.
     */
    public void verify() throws Exception
    {
        VerifierFactory.attach(new VerifierFactoryObserver()
        {
            public void update(java.lang.String s)
            {
                // System.out.println(s);
            }
        });
        Verifier verifier = VerifierFactory.getVerifier(className);
        VerificationResult result;
        result = verifier.doPass1();
        if (VerificationResult.VERIFIED_OK != result.getStatus())
        {
            JPOXLogger.ENHANCER.fatal(result.getMessage());
            return;
        }

        result = verifier.doPass2();
        if (VerificationResult.VERIFIED_OK != result.getStatus())
        {
            JPOXLogger.ENHANCER.fatal(result.getMessage());
            return;
        }
        Method methods[] = ((BCELClassMetaData) cmd).getClassGen().getMethods();
        JPOXLogger.ENHANCER.info("methods num:" + methods.length);
        for (int i = 0; i < methods.length; i++)
        {
            try
            {
                result = verifier.doPass3a(i);
                if (VerificationResult.VERIFIED_OK != result.getStatus())
                {
                    JPOXLogger.ENHANCER.fatal(result.getMessage());
                    // return;
                }
            }
            catch (Exception e)
            {
                JPOXLogger.ENHANCER.error(i + " " + methods[i].getName());
                /*
                 * String s[] = verifier.getMessages(); if (s != null) { for(int
                 * i2=0; i2<s.length; i2++) { JPOXLogger.ENHANCER.fatal(s[i2]); } }
                 */
            }
        }
    }

    /**
     * Check original class is already enhanced.
     * @return Return true if already enhanced class.
     */
    public boolean checkEnhanced()
    {
        if (cmd.getPersistenceModifier() != ClassPersistenceModifier.PERSISTENCE_CAPABLE &&
            cmd.getPersistenceModifier() != ClassPersistenceModifier.PERSISTENCE_AWARE)
        {
            return false;
        }

        if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_AWARE)
        {
            // The BCEL enhancer has no way of checking PersistenceAware classes
            // currently so just gives true
            return true;
        }

        String interfaceNames[] = newClass.getInterfaceNames();
        if (interfaceNames != null)
        {
            for (int i = 0; i < interfaceNames.length; i++)
            {
                if (interfaceNames[i].equals(CN_PersistenceCapable))
                {
                    return true;
                }
            }
        }
        Method methods[] = newClass.getMethods();
        if (methods != null)
        {
            for (int i = 0; i < methods.length; i++)
            {
                if (methods[i].getName().equals("jdoReplaceField"))
                {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Accessor for the AccessFlags for the <code>className</code> inner class
     * @param javaClass The original java class
     * @param className the inner class name
     * @return the access flags
     */
    private int getAccessFlagsForInnerClass(JavaClass javaClass, String className)
    {
        for (int i = 0; i < javaClass.getAttributes().length; i++)
        {
            if (javaClass.getAttributes()[i] instanceof InnerClasses)
            {
                InnerClasses innerClasses = (InnerClasses) javaClass.getAttributes()[i];
                for (int j = 0; j < innerClasses.getInnerClasses().length; j++)
                {
                    String name = constantPoolGen.getConstantPool().getConstantString(innerClasses.getInnerClasses()[j].getInnerClassIndex(),
                        Constants.CONSTANT_Class);
                    if (name.equals(className))
                    {
                        return innerClasses.getInnerClasses()[i].getInnerAccessFlags();
                    }
                }
            }
        }
        return -1;
    }

    /**
     * Check original class has default(no arg) constructor. <br>
     * Original class must has default(no arg) constructor.
     * @return Return null if this class has default constructor.
     */
    protected String checkHasDefaultConstructor()
    {
        Method methods[] = newClass.getMethods();
        for (int i = 0; i < methods.length; i++)
        {
            if (methods[i].getName().equals(Constants.CONSTRUCTOR_NAME))
            {
                if (methods[i].getSignature().equals("()V"))
                {
                    return null;
                }
            }
        }
        return LOCALISER.msg("Enhancer.RequiresDefaultConstructor", newClass.getClassName());
    }

    // ------------------------------ Enhancement --------------------------------

    /**
     * Method to enhance the classes
     * @return Whether the class was enhanced successfully
     */
    public boolean enhance()
    {
        if (cmd.getPersistenceModifier() != ClassPersistenceModifier.PERSISTENCE_CAPABLE &&
            cmd.getPersistenceModifier() != ClassPersistenceModifier.PERSISTENCE_AWARE)
        {
            return false;
        }

        initialise();

        // Check if already enhanced (PersistenceAware always come back as true so just enhance those)
        if (checkEnhanced() && cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
        {
            JPOXLogger.ENHANCER.info(LOCALISER.msg("Enhancer.ClassIsAlreadyEnhanced", newClass.getClassName()));
            return true;
        }

        // Check for obvious errors (why is this not done by checkEnhanced() ?)
        // Check if this is an inner class and is not static
        if (ClassUtils.isInnerClass(this.className) && (getAccessFlagsForInnerClass(oldClass, this.className) & Constants.ACC_STATIC) == 0)
        {
            JPOXLogger.ENHANCER.error(LOCALISER.msg("Enhancer.PersistentInnerClassMustBeStatic", newClass.getClassName()));
            return false;
        }

        try
        {
            if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
            {
                // Enhance for PersistenceCapable
                enhanceOriginalMethods();
                enhanceClass();
                enhanceFields();
                enhanceMethods();
                enhanceStaticInitializers();
            }
            else if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_AWARE)
            {
                // Enhance for PersistenceAware
                enhanceOriginalMethods();
            }
        }
        catch (Exception e)
        {
            // Catch any exceptions thrown by enhance process and send to log
            JPOXLogger.ENHANCER.error(LOCALISER.msg("Enhancer.ErrorEnhancingClass", cmd.getFullClassName(), e.getMessage()), e);
            return false;
        }

        update = true;
        return true;
    }

    /**
     * Method to enhance the class as a whole, providing the required interfaces
     * and adding any setters/getters for its fields
     */
    protected void enhanceClass()
    {
        if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
        {
            // Add PersistenceCapable
            class_addInterface(ClassEnhancer.CN_PersistenceCapable);

            // Add Detachable interface if required
            if (requiresDetachable())
            {
                class_addInterface(ClassEnhancer.CN_Detachable);
            }

            // Add setter/getter methods for the fields
            for (int i = 0; i < cmd.getNoOfMembers(); i++)
            {
                AbstractMemberMetaData f = cmd.getMetaDataForMemberAtRelativePosition(i);
                if (f.fieldBelongsToClass() && f.getPersistenceModifier() != FieldPersistenceModifier.NONE && !((BCELFieldPropertyMetaData)f).getEnhanceField().isSynthetic())
                {
                    enhanceSetter((BCELFieldPropertyMetaData)f);
                    enhanceGetter((BCELFieldPropertyMetaData)f);
                }
            }
        }
    }

    /**
     * Method to add the "implements {interface}" to the class description.
     * @param interfaceName Name of the interface to add.
     */
    protected void class_addInterface(String interfaceName)
    {
        if (JPOXLogger.ENHANCER.isDebugEnabled())
        {
            JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.AddInterface", interfaceName));
        }
        newClass.addInterface(interfaceName);
        newClass.update();
    }

    /**
     * Method to enhance the fields
     */
    protected void enhanceFields()
    {
        for (int i = 0; i < fieldsToAdd.size(); i++)
        {
            ClassField cf = (ClassField) fieldsToAdd.get(i);

            FieldGen gen = new FieldGen(cf.getAccess(), (Type) cf.getType(), cf.getName(), constantPoolGen);
            Field f = gen.getField();
            newClass.addField(f);
            BCELUtils.addSynthetic(f, constantPoolGen);
            if (JPOXLogger.ENHANCER.isDebugEnabled())
            {
                JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.AddField", f.getType() + " " + f.getName()));
            }
            newClass.update();
        }
    }

    /**
     * Method to enhance the methods of the class. Processes all methods in methodsToAdd, adding them to the class.
     */
    protected void enhanceMethods()
    {
        for (int i = 0; i < methodsToAdd.size(); i++)
        {
            Object o = methodsToAdd.get(i);
            if (o instanceof ClassMethod)
            {
                ClassMethod method = (ClassMethod) o;
                method.initialise();
                method.execute();
                method.close();
            }
            else if (o == null)
            {
                JPOXLogger.ENHANCER.error(LOCALISER.msg("Enhancer.CallbackIsNullError"));
            }
            else
            {
                JPOXLogger.ENHANCER.error(LOCALISER.msg("Enhancer.CallbackIsNotMethodBuilderError", o.getClass().getName()));
            }
        }
    }

    protected void enhanceStaticInitializers()
    {
        InstructionList il = null;
        InstructionFactory factory = new InstructionFactory(newClass);
        Method clinit = null;
        InstructionList ilOriginal = null;
        MethodGen methodGen = null;

        MethodGen methodGenOriginal = null;
        {
            Method methods[] = newClass.getMethods();
            for (int i = 0; i < methods.length; i++)
            {
                if (methods[i].getName().equals(Constants.STATIC_INITIALIZER_NAME))
                {
                    clinit = methods[i];
                    methodGenOriginal = new MethodGen(clinit, className, constantPoolGen);
                    ilOriginal = methodGenOriginal.getInstructionList();
                    break;
                }
            }
        }

        il = new InstructionList();
        methodGen = new MethodGen(Constants.ACC_STATIC, Type.VOID, Type.NO_ARGS, null, Constants.STATIC_INITIALIZER_NAME,
            className, il, constantPoolGen);

        if (ilOriginal != null)
        {
            // remove return instruction and add to top of static initialization block
            il.append(ilOriginal);

            InstructionHandle h[] = il.getInstructionHandles();
            if ("return".equalsIgnoreCase(h[h.length - 1].getInstruction().getName()))
            {
                try
                {
                    il.delete(h[h.length - 1]);
                }
                catch (TargetLostException e)
                {
                    InstructionHandle[] targets = e.getTargets();
                    for (int i2 = 0; i2 < targets.length; i2++)
                    {
                        InstructionTargeter[] targeters = targets[i2].getTargeters();
                        for (int j = 0; j < targeters.length; j++)
                        {
                            targeters[j].updateTarget(targets[i2], h[j]);
                        }
                    }
                }
            }

        }

        if (addSerialVersionUID != 0)
        {
            int svUidIndex = constantPoolGen.addLong(addSerialVersionUID);
            il.append(new LDC2_W(svUidIndex));
            il.append(factory.createPutStatic(className, FN_serialVersionUID, Type.LONG));
        }
        // field init MN_jdoFieldNamesInit
        il.append(factory.createInvoke(className, MN_FieldNamesInitMethod, new ArrayType(Type.STRING, 1), Type.NO_ARGS,
            Constants.INVOKESTATIC));
        il.append(factory.createPutStatic(className, FN_FieldNames, new ArrayType(Type.STRING, 1)));

        // field init MN_jdoFieldTypesInit
        il.append(factory.createInvoke(className, MN_FieldTypesInitMethod, new ArrayType(OT_CLASS, 1), Type.NO_ARGS, Constants.INVOKESTATIC));
        il.append(factory.createPutStatic(className, FN_FieldTypes, new ArrayType(OT_CLASS, 1)));

        // field init MN_FieldFlagsInitMethod
        il.append(factory.createInvoke(className, MN_FieldFlagsInitMethod, new ArrayType(Type.BYTE, 1), Type.NO_ARGS,
            Constants.INVOKESTATIC));
        il.append(factory.createPutStatic(className, FN_FieldFlags, new ArrayType(Type.BYTE, 1)));

        // field init MN_JdoFieldCounInitMethod
        il.append(factory.createInvoke(className, MN_JdoGetInheritedFieldCount, Type.INT, Type.NO_ARGS, Constants.INVOKESTATIC));
        il.append(factory.createPutStatic(className, FN_JdoInheritedFieldCount, Type.INT));

        // ___jdo$PersistenceCapableSuperclass =
        // ___jdo$PersistenceCapableSuperclassInit();
        il.append(factory.createInvoke(className, MN_JdoPersistenceCapableSuperclassInit, OT_CLASS, Type.NO_ARGS, Constants.INVOKESTATIC));
        il.append(factory.createPutStatic(className, FN_PersistenceCapableSuperclass, OT_CLASS));

        // class init
        /*
         * JDOImplHelper.registerClass( ___jdo$loadClass("fullclassname"),
         * ___jdo$fieldNames, ___jdo$fieldTypes, ___jdo$FieldFlags,
         * ___jdo$PersistenceCapableSuperclass, new ClassConstrutor());
         */
        il.append(new LDC(constantPoolGen.addString(className)));
        il.append(factory.createInvoke(className, MN_jdoLoadClass, OT_CLASS, new Type[]{Type.STRING}, Constants.INVOKESTATIC));
        il.append(factory.createGetStatic(className, FN_FieldNames, new ArrayType(Type.STRING, 1)));
        il.append(factory.createGetStatic(className, FN_FieldTypes, new ArrayType(OT_CLASS, 1)));
        il.append(factory.createGetStatic(className, FN_FieldFlags, new ArrayType(Type.BYTE, 1)));
        il.append(factory.createGetStatic(className, FN_PersistenceCapableSuperclass, OT_CLASS));

        if (((BCELClassMetaData) cmd).getClassGen().isAbstract())
        {
            // null
            il.append(InstructionConstants.ACONST_NULL);
        }
        else
        {
            // new ClassConstrutor()
            il.append(factory.createNew(new ObjectType(className)));
            il.append(InstructionConstants.DUP);
            il.append(factory.createInvoke(className, Constants.CONSTRUCTOR_NAME, Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
        }
        il.append(factory.createInvoke(CN_JDOImplHelper, "registerClass", Type.VOID, new Type[]{OT_CLASS, new ArrayType(Type.STRING, 1),
                new ArrayType(OT_CLASS, 1), new ArrayType(Type.BYTE, 1), OT_CLASS, OT_PersistenceCapable}, Constants.INVOKESTATIC));

        staticInitializerAppend(factory, il);
        il.append(InstructionConstants.RETURN);

        // newClass.addMethod(method);
        if (clinit != null)
        {
            newClass.removeMethod(clinit);
        }
        methodGen.setMaxLocals();
        methodGen.setMaxStack();
        Method method = methodGen.getMethod();
        {
            Method allMethod[] = newClass.getMethods();
            newClass.replaceMethod(allMethod[0], method);
            newClass.addMethod(allMethod[0]);
        }
        // BCELUtil.addSynthetic(method, constantPoolGen);
        il.dispose();
    }

    protected void staticInitializerAppend(InstructionFactory factory, InstructionList il)
    {
        // Do nothing
    }

    protected void enhanceOriginalMethod(Method m)
    {
        boolean isDebugEnabled = JPOXLogger.ENHANCER.isDebugEnabled();
        if (BCELUtils.isSynthetic(m))
        {
            // Skip synthetic method
            return;
        }

        MethodGen methodGen = new MethodGen(m, className, constantPoolGen);
        InstructionList il = methodGen.getInstructionList();
        if ((il == null) || (il.size() == 0))
        {
            return;
        }
        InstructionFactory factory = new InstructionFactory(newClass);
        boolean isCloneMethod = ("clone".equals(m.getName()) && ((m.getArgumentTypes() == null) || (m.getArgumentTypes().length == 0)));
        boolean change = false;
        InstructionHandle ih = il.getStart();
        while (ih != null)
        {
            Instruction i = ih.getInstruction();

            if ((i instanceof GETFIELD) || (i instanceof PUTFIELD))
            {
                Field f;
                FieldInstruction field = (FieldInstruction) i;
                Constant c = m.getConstantPool().getConstant(field.getIndex());
                ConstantFieldref fieldRef = (ConstantFieldref) c;

                ConstantClass cclass = (ConstantClass) m.getConstantPool().getConstant(fieldRef.getClassIndex());
                ConstantUtf8 utfClassName = (ConstantUtf8) m.getConstantPool().getConstant(cclass.getNameIndex());
                String utfClassNameString = StringUtils.replaceAll(utfClassName.getBytes().toString(), "/", ".");
                JavaClass fieldJavaClass = null;
                try
                {
                    fieldJavaClass = Repository.lookupClass(utfClassNameString);
                }
                catch (Throwable ex)
                {
                    // catch Throwable, so it is compatible with latest BCEL changes in methods signature.
                    // It nows raises ClassNotFoundException. In order to be able to compible this code
                    // with bcel-5.1 or bcel-5.1+, we catch as throwable
                    JPOXLogger.ENHANCER.error(LOCALISER.msg("Enhancer.ClassNotFound", utfClassNameString, ex));
                    throw new JDOFatalException(LOCALISER.msg("Enhancer.ClassNotFound", utfClassNameString, ex));
                }
                if (fieldJavaClass == null)
                {
                    throw new JDOFatalException(LOCALISER.msg("Enhancer.ClassNotFound", utfClassNameString,
                        new NullPointerException()));
                }

                f = BCELUtils.getFieldByName(field.getName(constantPoolGen), fieldJavaClass);
                if (f == null)
                {
                    String message = LOCALISER.msg("Enhancer.FieldIsNull", className, m.getName(), field.getName(constantPoolGen));
                    JPOXLogger.ENHANCER.error(message);
                    throw new NullPointerException(message);
                }

                ClassGen cg = BCELUtils.getClassByFieldByName(field.getName(constantPoolGen), fieldJavaClass);
                BCELFieldPropertyMetaData fieldConfig = null;
                BCELClassMetaData jdoConfigClass = ((BCELClassMetaData) cmd);

                // the accessing class is not this
                if (!cg.getClassName().equals(newClass.getClassName()))
                {
                    jdoConfigClass = (BCELClassMetaData) cmd.getPackageMetaData().getFileMetaData().getMetaDataManager().getMetaDataForClass(
                        cg.getClassName(), clr);
                }
                if( jdoConfigClass != null )
                {
                    AbstractMemberMetaData apmd = jdoConfigClass.findField(f);

                    if (apmd == null)
                    {
                        //check if a property(getter,setter) exists with this field name
                        if( jdoConfigClass.findProperty(f)==null )
                        {
                            //no fields netiher properties in the class, so something is wrong
                            String message = LOCALISER.msg("Enhancer.FieldConfigIsNullError", className + "." + f.getName());
                            JPOXLogger.ENHANCER.fatal(message);
                            throw new RuntimeException(message);
                        }
                    }
                    if( apmd != null && apmd.getPersistenceModifier() != FieldPersistenceModifier.NONE )
                    {
                        // do nothing
                    }
                    if (!isFieldAccessInPersistenceCapableClass(ih, m.getConstantPool()))
                    {
                        // do nothing
                    }
                    else if (fieldConfig != null && fieldConfig.getJdoFieldFlag() == 0)
                    {
                        // do nothing
                    }
                    else if (f.isStatic() || f.isFinal())
                    {
                        // do nothing
                    }
                    else if (BCELUtils.isSynthetic(f))
                    {
                        // do nothing
                    }
                    else
                    {
                        if (isDebugEnabled)
                        {
                            JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.EnhanceOriginalMethod",
                                className + "." + m.getName(), f.getName()));
                        }
                        if( apmd != null && !apmd.isProperty() && apmd.getPersistenceModifier() != FieldPersistenceModifier.NONE)
                        {
                            //properties do not use jdoXXX methods
                            if (i instanceof GETFIELD)
                            {
                                ih.setInstruction(factory.createInvoke(cg.getClassName(), "jdo" + BCELUtils.getGetterName(f),
                                    field.getType(constantPoolGen), new Type[]{new ObjectType(cg.getClassName())}, Constants.INVOKESTATIC));
                            }
                            else
                            {
                                ih.setInstruction(factory.createInvoke(cg.getClassName(), "jdo" + BCELUtils.getSetterName(f),
                                    Type.VOID, new Type[]{new ObjectType(cg.getClassName()), field.getType(constantPoolGen)},
                                    Constants.INVOKESTATIC));
                            }
                            change = true;
                        }
                    }
                }
            }
            else if (isCloneMethod && (i instanceof INVOKESPECIAL))
            {
                // If this is the clone method and is the root persistence capable, call jdoSuperClone
                INVOKESPECIAL is = (INVOKESPECIAL) i;
                if ((cmd.getPersistenceCapableSuperclass() == null) && ("clone".equals(is.getMethodName(constantPoolGen))) &&
                    ("()Ljava/lang/Object;".equals(is.getSignature(constantPoolGen))))
                {
                    ih.setInstruction(factory.createInvoke(className, ClassEnhancer.MN_JdoSuperClone, Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
                    if (isDebugEnabled)
                    {
                        JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.EnhanceOriginalMethod", className + "." + m.getName(), "super.clone()"));
                    }
                    change = true;
                }
            }

            ih = ih.getNext();
        }
        if (change)
        {
            methodGen.setMaxLocals();
            methodGen.setMaxStack();
            newClass.replaceMethod(m, methodGen.getMethod());
        }
    }

    /**
     * Takes the original getXXX, setXXX method and takes the code and creates a jdoGetXXX, jdoSetXXX
     * with the exact same code. The jdoGetXXX, jdoSetXXX are not static (unlike for persistent fields).
     * e.g.
     * <code>
     * public String getName()
     * {
     *    return name;
     * }
     * </code>
     * The generated method is:
     * <code>
     * public String jdoGetName()
     * {
     *    return name;
     * }
     * </code>
     *
     * @param m The method
     */
    protected void enhancePropertyAccessor(Method m)
    {
        if (BCELUtils.isSynthetic(m))
        {
            // Skip synthetic method
            return;
        }

        String getterName = ClassUtils.getFieldNameForJavaBeanGetter(m.getName());
        String setterName = ClassUtils.getFieldNameForJavaBeanSetter(m.getName());
        String newMethodName = null;
        String propertyName = null;

        if (getterName != null)
        {
            newMethodName = "jdo"+BCELUtils.getGetterName(getterName);
            propertyName = getterName;
        }
        else if (setterName != null)
        {
            newMethodName = "jdo"+BCELUtils.getSetterName(setterName);
            propertyName = setterName;
        }
        else
        {
            // Not valid getXXX, setXXX method
            return;
        }

        AbstractMemberMetaData apmd = cmd.getMetaDataForMember(propertyName);
        if (apmd == null || !apmd.isProperty() || apmd.getPersistenceModifier() == FieldPersistenceModifier.NONE)
        {
            return;
        }

        MethodGen methodGen = new MethodGen(m, className, constantPoolGen);
        methodGen.setName(newMethodName);
        methodGen.setMaxLocals();
        methodGen.setMaxStack();
        newClass.addMethod(methodGen.getMethod());
    }

    protected void enhanceOriginalMethods()
    {
        Method methods[] = newClass.getMethods();
        for (int i = 0; i < methods.length; i++)
        {
            // see JSR12 version 1.0 10.3
            if ("jdoPreClear".equals(methods[i].getName()) || "jdoPostLoad".equals(methods[i].getName()))
            {
                // Do nothing
            }
            else if ("readObject".equals(methods[i].getName()) &&
                    (methods[i].getSignature().equals("(Ljava/io/ObjectOutputStream;)V") ||
                     methods[i].getSignature().equals("(Ljava/io/ObjectInputStream;)V")))
            {
                // Do nothing
            }
            else
            {
                // Enhance original methods use of persistent fields/properties
                enhanceOriginalMethod(methods[i]);

                // For any methods that are persistent property setter/getter create enhanced setter/getter
                enhancePropertySetter(methods[i]);
                enhancePropertyGetter(methods[i]);

                // For any methods that are persistent property setter/getter create jdoSetXXX, jdoGetXXX with orig code
                enhancePropertyAccessor(methods[i]);
            }
        }
    }

    /**
     * Method to take the method and create a valid setXXX when the method is a setter for a persistent property.
     * @param m The method
     */
    protected void enhancePropertySetter(Method m)
    {
        String name = ClassUtils.getFieldNameForJavaBeanSetter(m.getName());
        AbstractMemberMetaData apmd = cmd.getMetaDataForMember(name);
        if (apmd != null && apmd.getPersistenceModifier() != FieldPersistenceModifier.NONE && apmd.isProperty())
        {
            if (((apmd.getJdoFieldFlag() & PersistenceCapable.MEDIATE_WRITE) == PersistenceCapable.MEDIATE_WRITE)||
                ((apmd.getJdoFieldFlag() & PersistenceCapable.CHECK_WRITE) == PersistenceCapable.CHECK_WRITE))
            {
                PropertySetterMethod setter = new PropertySetterMethod(m, this.className, constantPoolGen, newClass,
                    m.getArgumentTypes(), (BCELFieldPropertyMetaData)apmd, this);
                setter.execute();
            }
        }
    }

    /**
     * Method to take the method and create a valid getXXX when the method is a getter for a persistent property.
     * @param m The method
     */
    protected void enhancePropertyGetter(Method m)
    {
        String name = ClassUtils.getFieldNameForJavaBeanGetter(m.getName());
        AbstractMemberMetaData apmd = cmd.getMetaDataForMember(name);
        if (apmd != null && apmd.getPersistenceModifier() != FieldPersistenceModifier.NONE && apmd.isProperty())
        {
            if (((apmd.getJdoFieldFlag() & PersistenceCapable.MEDIATE_READ) == PersistenceCapable.MEDIATE_READ)||
                ((apmd.getJdoFieldFlag() & PersistenceCapable.CHECK_READ) == PersistenceCapable.CHECK_READ))
            {
                PropertyGetterMethod getter = new PropertyGetterMethod(m, this.className, constantPoolGen, newClass,
                    m.getArgumentTypes(), (BCELFieldPropertyMetaData)apmd, this);
                getter.execute();
            }
        }
    }

    /**
     * check if the getfield/setfield is suitable to be enhanced. The enhancer will change the code from
     * something like: "fieldA = fieldB" to "jdoSetfieldA(fieldB)" or "jdoSetfieldA(jdoGetfieldB())"
     * "fieldA = fieldB" to "jdoSetfieldA(fieldB)" or "jdoSetfieldA(jdoGetfieldB())"
     * @param ih the getfield or setfield instruction
     * @param cp the constant pool
     * @return
     */
    private String getClassNameForFieldAccess(InstructionHandle ih, ConstantPool cp)
    {
        Constant c = cp.getConstant(((CPInstruction) ih.getInstruction()).getIndex());
        if (c instanceof ConstantFieldref)
        {
            ConstantFieldref fieldRef = (ConstantFieldref) c;
            ConstantClass cclass = (ConstantClass) cp.getConstant(fieldRef.getClassIndex());
            ConstantUtf8 className = (ConstantUtf8) cp.getConstant(cclass.getNameIndex());
            return StringUtils.replaceAll(className.getBytes().toString(), "/", ".");
        }
        return null;
    }

    /**
     * Check whether the field access in the instruction argument ih is in a PersistenceCapable class
     * @param ih the getfield or setfield instruction
     * @param cp the constant pool
     * @return true if the access is in a PersistenceCapable class
     */
    private boolean isFieldAccessInPersistenceCapableClass(InstructionHandle ih, ConstantPool cp)
    {
        String className = getClassNameForFieldAccess(ih, cp);
        if (className == null)
        {
            return false;
        }
        return isPersistenceCapable(className);
    }

    /**
     * This method adds to the generated class the jdoSet methods
     * @param fieldConfig
     */
    protected void enhanceSetter(BCELFieldPropertyMetaData fieldConfig)
    {
        BCELMember f = fieldConfig.getEnhanceField();
        String methodName = BCELUtils.getSetterName(f.getName());
        if (f.isMethod())
        {
            methodName = f.getName();
        }
        BCELClassMethod callback = null;
        byte jdoFlag = fieldConfig.getJdoFieldFlag();

        if (f.isFinal() || f.isStatic())
        {
            return;
        }

        if (FieldPersistenceModifier.NONE.equals(fieldConfig.getPersistenceModifier()))
        {
            return;
        }
        if (((AbstractMemberMetaData)fieldConfig).isProperty())
        {
            return;
        }
        if ((jdoFlag & PersistenceCapable.MEDIATE_WRITE) == PersistenceCapable.MEDIATE_WRITE)
        {
            callback = new MediateWriteMethod("jdo" + methodName,
                    (f.isPublic() ? Constants.ACC_PUBLIC : 0) |
                    (f.isProtected() ? Constants.ACC_PROTECTED : 0) |
                    (f.isPrivate() ? Constants.ACC_PRIVATE : 0) | Constants.ACC_STATIC,
                    Type.VOID, new Type[]{this.classType, f.getType()}, new String[]{"objPC", f.getName() + "_m"}, true, this,
                    fieldConfig);
        }
        else if ((jdoFlag & PersistenceCapable.CHECK_WRITE) == PersistenceCapable.CHECK_WRITE)
        {
            callback = new CheckWriteMethod("jdo" + methodName,
                    (f.isPublic() ? Constants.ACC_PUBLIC : 0) |
                    (f.isProtected() ? Constants.ACC_PROTECTED : 0) |
                    (f.isPrivate() ? Constants.ACC_PRIVATE : 0) | Constants.ACC_STATIC,
                    Type.VOID, new Type[]{this.classType, f.getType()}, new String[]{"objPC", f.getName() + "_c"}, true, this,
                    fieldConfig);
        }
        else
        {
            callback = new NormalSetMethod("jdo" + methodName,
                (f.isPublic() ? Constants.ACC_PUBLIC : 0) |
                (f.isProtected() ? Constants.ACC_PROTECTED : 0) |
                (f.isPrivate() ? Constants.ACC_PRIVATE : 0) | Constants.ACC_STATIC,
                Type.VOID, new Type[]{this.classType, f.getType()}, new String[]{"objPC", f.getName() + "_n"}, true, this,
                fieldConfig);
        }

        if (callback != null)
        {
            methodsToAdd.add(callback);
        }
    }

    /**
     * This method adds to the generated class the jdoGet methods
     * @param fieldConfig
     */
    protected void enhanceGetter(BCELFieldPropertyMetaData fieldConfig)
    {
        BCELMember f = fieldConfig.getEnhanceField();
        String methodName = BCELUtils.getGetterName(f.getName());
        if( f.isMethod() )
        {
            methodName = f.getName();
        }
        BCELClassMethod callback = null;
        byte jdoFlag = fieldConfig.getJdoFieldFlag();

        if (f.isFinal() || f.isStatic())
        {
            return;
        }

        if (FieldPersistenceModifier.NONE.equals(fieldConfig.getPersistenceModifier()))
        {
            return;
        }
        if (((AbstractMemberMetaData)fieldConfig).isProperty())
        {
            return;
        }

        if ((jdoFlag & PersistenceCapable.MEDIATE_READ) == PersistenceCapable.MEDIATE_READ)
        {
            callback = new MediateReadMethod("jdo" + methodName,
                    (f.isPublic() ? Constants.ACC_PUBLIC : 0) |
                    (f.isProtected() ? Constants.ACC_PROTECTED : 0) |
                    (f.isPrivate() ? Constants.ACC_PRIVATE : 0) | Constants.ACC_STATIC,
                    f.getType(), new Type[]{this.classType}, new String[]{"objPC"}, true, this, fieldConfig);
        }
        else if ((jdoFlag & PersistenceCapable.CHECK_READ) == PersistenceCapable.CHECK_READ)
        {
            callback = new CheckReadMethod("jdo" + methodName,
                (f.isPublic() ? Constants.ACC_PUBLIC : 0) |
                (f.isProtected() ? Constants.ACC_PROTECTED : 0) |
                (f.isPrivate() ? Constants.ACC_PRIVATE : 0) | Constants.ACC_STATIC,
                f.getType(), new Type[]{this.classType}, new String[]{"objPC"}, true, this, fieldConfig);
        }
        else
        {
            callback = new NormalGetMethod("jdo" + methodName,
                (f.isPublic() ? Constants.ACC_PUBLIC : 0) |
                (f.isProtected() ? Constants.ACC_PROTECTED : 0) |
                (f.isPrivate() ? Constants.ACC_PRIVATE : 0) | Constants.ACC_STATIC, f.getType(), new Type[]{this.classType},
                new String[]{"objPC"}, true, this, fieldConfig);
        }

        methodsToAdd.add(callback);
    }

    /**
     * Access the class in byte array format
     * @return the class in byte array format
     */
    public byte[] getBytes()
    {
        return newClass.getJavaClass().getBytes();
    }

    /**
     * Compute the serialVersionUID
     * @author unknown
     */
    public class SerialVersionUID
    {

        private final Comparator FIELD_OR_METHOD_COMPARATOR = new FieldOrMethodComparator();

        /**
         * This method computes the serialVersionUID of a BCEL JavaClass in the same way that the
         * java.io.ObjectStreamClass class computes it for a java.lang.Class.
         * <p>
         * This method is a port of version 1.98 of the ObjectStreamClass's computeDefaultSUID method.
         * <p>
         * Compute a hash for the specified class. Incrementally add items to the hash accumulating in the digest
         * stream. Fold the hash into a long. Use the SHA secure hash function.
         * @param javaClass the class to compute the serialversionUID from
         * @return the serial version uid
         * @throws InternalError
         * @throws SecurityException
         */
        public long computeSerialVersionUID(JavaClass javaClass)
        {
            try
            {
                ByteArrayOutputStream bout = new ByteArrayOutputStream();
                DataOutputStream dout = new DataOutputStream(bout);
                Method[] methods = javaClass.getMethods();
                Field[] fields = javaClass.getFields();

                dout.writeUTF(javaClass.getClassName());
                int classMods = javaClass.getAccessFlags();
                classMods &= (Modifier.PUBLIC | Modifier.FINAL | Modifier.INTERFACE | Modifier.ABSTRACT);
                // compensate for javac bug in which ABSTRACT bit was set for an
                // interface only if the interface declared methods
                if ((classMods & Modifier.INTERFACE) != 0)
                {
                    classMods = (methods.length > 0) ? (classMods | Modifier.ABSTRACT) : (classMods & ~Modifier.ABSTRACT);
                }
                dout.writeInt(classMods);
                if (true)
                {
                    // compensate for change in 1.2FCS in which
                    // Class.getInterfaces() was modified to return Cloneable and
                    // Serializable for array classes.
                    String[] ifaceNames = javaClass.getInterfaceNames();
                    Arrays.sort(ifaceNames);
                    for (int i = 0; i < ifaceNames.length; i++)
                    {
                        dout.writeUTF(ifaceNames[i]);
                    }
                }
                // ----------------------------------------------------------------
                // fields
                Arrays.sort(fields, FIELD_OR_METHOD_COMPARATOR);
                for (int i = 0; i < fields.length; i++)
                {
                    Field field = fields[i];
                    int mods = fields[i].getAccessFlags();
                    if (((mods & Modifier.PRIVATE) == 0) || ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0))
                    {
                        dout.writeUTF(field.getName());
                        dout.writeInt(mods);
                        dout.writeUTF(field.getSignature());
                    }
                }
                // ----------------------------------------------------------------
                // methods
                Arrays.sort(methods, FIELD_OR_METHOD_COMPARATOR);
                for (int i = 0; i < methods.length; i++)
                {
                    Method method = methods[i];
                    int mods = method.getAccessFlags();
                    if ((mods & Modifier.PRIVATE) == 0)
                    {
                        dout.writeUTF(method.getName());
                        dout.writeInt(mods);
                        dout.writeUTF(method.getSignature().replace('/', '.'));
                    }
                }
                // ----------------------------------------------------------------
                // closing
                dout.flush();
                MessageDigest md = MessageDigest.getInstance("SHA");
                byte[] hashBytes = md.digest(bout.toByteArray());
                long hash = 0;
                for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--)
                {
                    hash = (hash << 8) | (hashBytes[i] & 0xFF);
                }
                return hash;
            }
            catch (IOException ex)
            {
                throw new InternalError();
            }
            catch (NoSuchAlgorithmException ex)
            {
                throw new SecurityException(ex.getMessage());
            }
        }

        private class FieldOrMethodComparator implements Comparator
        {

            public int compare(Object o1, Object o2)
            {
                FieldOrMethod fom1 = (FieldOrMethod) o1;
                FieldOrMethod fom2 = (FieldOrMethod) o2;
                int comp = fom1.getName().compareTo(fom2.getName());
                if (comp == 0)
                {
                    comp = fom1.getSignature().compareTo(fom2.getSignature());
                }
                return comp;
            }
        }
    }
}
TOP

Related Classes of org.jpox.enhancer.bcel.BCELClassEnhancer

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.