Package org.jpox.enhancer.asm

Source Code of org.jpox.enhancer.asm.JdoClassAdapter

/**********************************************************************
Copyright (c) 2007 Andy Jefferson 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:
    ...
**********************************************************************/
package org.jpox.enhancer.asm;

import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;

import javax.jdo.spi.Detachable;
import javax.jdo.spi.PersistenceCapable;

import org.jpox.enhancer.ClassEnhancer;
import org.jpox.enhancer.ClassField;
import org.jpox.enhancer.asm.method.CheckRead;
import org.jpox.enhancer.asm.method.CheckWrite;
import org.jpox.enhancer.asm.method.DefaultConstructor;
import org.jpox.enhancer.asm.method.InitClass;
import org.jpox.enhancer.asm.method.MediateRead;
import org.jpox.enhancer.asm.method.MediateWrite;
import org.jpox.enhancer.asm.method.NormalGet;
import org.jpox.enhancer.asm.method.NormalSet;
import org.jpox.enhancer.asm.method.WriteObject;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ClassPersistenceModifier;
import org.jpox.metadata.FieldPersistenceModifier;
import org.jpox.util.ClassUtils;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
import org.jpox.util.StringUtils;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
* Adapter visitor class for providing enhancement of an existing class using ASM.
* Is created with its own ClassWriter, and is passed to a ClassReader to visit the class.
* All parts of the class to be enhanced are fed through the different visitXXX methods here
* allowing intervention to either enhance an existing method, or to add on new fields/methods/interfaces.
*
* @version $Revision: 1.9 $
*/
public class JdoClassAdapter extends ClassAdapter
{
    /** Localisation of messages */
    protected static Localiser LOCALISER = Localiser.getInstance("org.jpox.enhancer.Localisation",
        ClassEnhancer.class.getClassLoader());

    /** The underlying enhancer. */
    protected ASMClassEnhancer enhancer;

    /** Whether a default constructor is present. Set if found, and then processed in visitEnd. */
    protected boolean hasDefaultConstructor = false;

    /** Whether the field serialVersionUID is present. Set if found, and procedded in visitEnd. */
    protected boolean hasSerialVersionUID = false;

    /** Whether the method writeObject(ObjectOutputStream) is present. Set if found, and procedded in visitEnd. */
    protected boolean hasWriteObject = false;

    /** Whether the class already has a static init block. Set if found, and processed in visitEnd. */
    protected boolean hasStaticInitialisation = false;

    /**
     * Constructor.
     * If the writer is null it means we just have to check the enhancement status
     * @param cv The writer visitor
     */
    public JdoClassAdapter(ClassVisitor cv, ASMClassEnhancer enhancer)
    {
        super(cv);
        this.enhancer = enhancer;
    }

    /**
     * Method called to visit the header of the class.
     * @param version Version of this class
     * @param access Access for the class
     * @param name name of the class
     * @param signature Signature of the class
     * @param superName Superclass name (if any)
     * @param interfaces Interface(s) implemented
     */
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
    {
        if (enhancer.getClassMetaData().getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
        {
            // Check if the class already implements PersistenceCapable/Detachable interfaces
            boolean alreadyPersistenceCapable = false;
            boolean alreadyDetachable = false;
            boolean needsPersistenceCapable = false;
            boolean needsDetachable = false;
            int numInterfaces = 0;
            if (interfaces != null && interfaces.length > 0)
            {
                numInterfaces = interfaces.length;
                for (int i=0;i<interfaces.length;i++)
                {
                    if (interfaces[i].equals(ASMUtils.ACN_Detachable))
                    {
                        alreadyDetachable = true;
                    }
                    if (interfaces[i].equals(ASMUtils.ACN_PersistenceCapable))
                    {
                        alreadyPersistenceCapable = true;
                    }
                }
            }
            if (!alreadyDetachable && enhancer.getClassMetaData().isDetachable())
            {
                numInterfaces++;
                needsDetachable = true;
            }
            if (!alreadyPersistenceCapable)
            {
                numInterfaces++;
                needsPersistenceCapable = true;
            }

            String[] intfs = interfaces;
            if (needsDetachable || needsPersistenceCapable)
            {
                // Allocate updated array of interfaces
                intfs = new String[numInterfaces];
                int position = 0;
                if (interfaces != null && interfaces.length > 0)
                {
                    for (int i=0;i<interfaces.length;i++)
                    {
                        intfs[position++] = interfaces[i];
                    }
                }

                if (needsDetachable)
                {
                    intfs[position++] = ASMUtils.ACN_Detachable;
                    if (JPOXLogger.ENHANCER.isDebugEnabled())
                    {
                        JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.AddInterface", Detachable.class.getName()));
                    }
                }
                if (needsPersistenceCapable)
                {
                    intfs[position++] = ASMUtils.ACN_PersistenceCapable;
                    if (JPOXLogger.ENHANCER.isDebugEnabled())
                    {
                        JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.AddInterface", PersistenceCapable.class.getName()));
                    }
                }
            }
            cv.visit(version, access, name, signature, superName, intfs);
        }
        else
        {
            cv.visit(version, access, name, signature, superName, interfaces);
        }
    }

    /**
     * Method called when a field of the class is visited.
     * @param access Access type
     * @param name Name of the field
     * @param desc Descriptor of the field
     * @param signature Signature of the field
     * @param value Value of the field
     * @return FieldVisitor
     */
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)
    {
        if (name.equals(ClassEnhancer.FN_serialVersionUID))
        {
            // Has serialVersionUID field for use in serialisation
            hasSerialVersionUID = true;
        }
        return super.visitField(access, name, desc, signature, value);
    }

    /**
     * Method called when a method of the class is visited.
     * @param access Access for the method
     * @param name Name of the method
     * @param desc Descriptor
     * @param signature Signature
     * @param exceptions Exceptions that this method is declared to throw
     * @return Visitor to visit this (or null if not wanting to visit it)
     */
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
    {
        if (name.equals("<init>") && desc != null && desc.equals("()V"))
        {
            // Check for default constructor
            hasDefaultConstructor = true;
        }
        if (name.equals("writeObject") && desc != null && desc.equals("(Ljava/io/ObjectOutputStream;)V"))
        {
            // Has writeObject() for use in serialisation
            hasWriteObject = true;
        }
        if (name.equals("<clinit>") && desc != null && desc.equals("()V"))
        {
            hasStaticInitialisation = true;
        }

        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
        if (mv == null)
        {
            return null;
        }

        if (name.equals("jdoPreClear") || name.equals("jdoPostLoad"))
        {
            // jdoPreClear/jdoPostLoad should not be enhanced (JDO2 spec [10.1, 10.3]
            return mv;
        }
        else if (name.equals("readObject") &&
            (desc.equals("(Ljava/io/ObjectOutputStream;)V") || desc.equals("(Ljava/io/ObjectInputStream;)V")))
        {
            // readObject(ObjectInputStream), readObject(ObjectOutputStream) should not be enhanced (JDO2 spec [21.6])
            return mv;
        }

        String propGetterName = ClassUtils.getFieldNameForJavaBeanGetter(name);
        String propSetterName = ClassUtils.getFieldNameForJavaBeanSetter(name);
        if (propGetterName != null)
        {
            AbstractMemberMetaData mmd = enhancer.getClassMetaData().getMetaDataForMember(propGetterName);
            if (mmd != null && mmd.isProperty() && mmd.getPersistenceModifier() != FieldPersistenceModifier.NONE)
            {
                // Property getter method "getXXX" - generated jdoGetXXX
                return new JdoPropertyGetterAdapter(mv, enhancer, name, desc, mmd, cv);
            }
        }
        else if (propSetterName != null)
        {
            AbstractMemberMetaData mmd = enhancer.getClassMetaData().getMetaDataForMember(propSetterName);
            if (mmd != null && mmd.isProperty() && mmd.getPersistenceModifier() != FieldPersistenceModifier.NONE)
            {
                // Property setter method "setXXX" - generates jdoSetXXX
                return new JdoPropertySetterAdapter(mv, enhancer, name, desc, mmd, cv);
            }
        }

        // normal method, so just enhance it
        return new JdoMethodAdapter(mv, enhancer, name, desc);
    }

    /**
     * Method called at the end of the class.
     */
    public void visitEnd()
    {
        if (enhancer.getClassMetaData().getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
        {
            // Add any new fields
            List fields = enhancer.getFieldsList();
            Iterator fieldsIter = fields.iterator();
            while (fieldsIter.hasNext())
            {
                ClassField field = (ClassField)fieldsIter.next();
                if (JPOXLogger.ENHANCER.isDebugEnabled())
                {
                    JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.AddField",
                        ((Class)field.getType()).getName() + " " + field.getName()));
                }
                cv.visitField(field.getAccess(), field.getName(), Type.getDescriptor((Class)field.getType()), null, null);
            }

            if (!hasStaticInitialisation)
            {
                // Add a static initialisation block for the class since nothing added yet
                InitClass method = InitClass.getInstance(enhancer);
                method.initialise(cv);
                method.execute();
                method.close();
            }

            if (!hasDefaultConstructor)
            {
                // Add a default constructor
                DefaultConstructor ctr = DefaultConstructor.getInstance(enhancer);
                ctr.initialise(cv);
                ctr.execute();
                ctr.close();
            }

            // Add any new methods
            List methods = enhancer.getMethodsList();
            Iterator methodsIter = methods.iterator();
            while (methodsIter.hasNext())
            {
                ASMClassMethod method = (ASMClassMethod)methodsIter.next();

                method.initialise(cv);
                method.execute();
                method.close();
            }

            if (Serializable.class.isAssignableFrom(enhancer.cls))
            {
                // Class is Serializable
                if (!hasSerialVersionUID)
                {
                    // Needs "serialVersionUID" field
                    Long uid = null;
                    try
                    {
                        uid = new Long(ObjectStreamClass.lookup(enhancer.getClassEnhanced()).getSerialVersionUID());
                    }
                    catch (Throwable e)
                    {
                        JPOXLogger.ENHANCER.warn(StringUtils.getStringFromStackTrace(e));
                    }
                    ClassField cf = new ClassField(enhancer, ClassEnhancer.FN_serialVersionUID,
                        Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, long.class, uid);
                    if (JPOXLogger.ENHANCER.isDebugEnabled())
                    {
                        JPOXLogger.ENHANCER.debug(LOCALISER.msg("Enhancer.AddField",
                            ((Class)cf.getType()).getName() + " " + cf.getName()));
                    }
                    cv.visitField(cf.getAccess(), cf.getName(), Type.getDescriptor((Class)cf.getType()), null, cf.getInitialValue());
                }
                if (!hasWriteObject)
                {
                    ASMClassMethod method = WriteObject.getInstance(enhancer);
                    method.initialise(cv);
                    method.execute();
                    method.close();
                }
            }

            // Add jdoGetXXX, jdoSetXXX for each of the (managed) fields/properties
            AbstractMemberMetaData[] fmds = enhancer.getClassMetaData().getManagedMembers();
            for (int i = 0; i < fmds.length; i++)
            {
                if (fmds[i].isStatic() || fmds[i].isFinal() ||
                    fmds[i].getPersistenceModifier() == FieldPersistenceModifier.NONE)
                {
                    // Field/Property is not persistent so ignore
                    continue;
                }

                byte jdoFlag = fmds[i].getJdoFieldFlag();
                ASMClassMethod getMethod = null;
                ASMClassMethod setMethod = null;
                if (fmds[i].isProperty())
                {
                    // jdoGetXXX, jdoSetXXX for property are generated when processing existing getXXX, setXXX methods
                }
                else
                {
                    // Generate jdoGetXXX, jdoSetXXX for field
                    if ((jdoFlag & PersistenceCapable.MEDIATE_READ) == PersistenceCapable.MEDIATE_READ)
                    {
                        getMethod = new MediateRead(enhancer, fmds[i]);
                    }
                    else if ((jdoFlag & PersistenceCapable.CHECK_READ) == PersistenceCapable.CHECK_READ)
                    {
                        getMethod = new CheckRead(enhancer, fmds[i]);
                    }
                    else
                    {
                        getMethod = new NormalGet(enhancer, fmds[i]);
                    }

                    if ((jdoFlag & PersistenceCapable.MEDIATE_WRITE) == PersistenceCapable.MEDIATE_WRITE)
                    {
                        setMethod = new MediateWrite(enhancer, fmds[i]);
                    }
                    else if ((jdoFlag & PersistenceCapable.CHECK_WRITE) == PersistenceCapable.CHECK_WRITE)
                    {
                        setMethod = new CheckWrite(enhancer, fmds[i]);
                    }
                    else
                    {
                        setMethod = new NormalSet(enhancer, fmds[i]);
                    }
                }

                if (getMethod != null)
                {
                    getMethod.initialise(cv);
                    getMethod.execute();
                    getMethod.close();
                }
                if (setMethod != null)
                {
                    setMethod.initialise(cv);
                    setMethod.execute();
                    setMethod.close();
                }
            }
        }
        cv.visitEnd();
    }
}
TOP

Related Classes of org.jpox.enhancer.asm.JdoClassAdapter

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.