You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by st...@apache.org on 2016/09/13 17:29:02 UTC

svn commit: r1760580 [4/5] - in /openjpa/branches/fb-3.0-asm: openjpa-examples/openbooks/ openjpa-kernel/src/main/java/org/apache/openjpa/ant/ openjpa-kernel/src/main/java/org/apache/openjpa/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/enhance...

Added: openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerSerp.java
URL: http://svn.apache.org/viewvc/openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerSerp.java?rev=1760580&view=auto
==============================================================================
--- openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerSerp.java (added)
+++ openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerSerp.java Tue Sep 13 17:29:01 2016
@@ -0,0 +1,4950 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.    
+ */
+package org.apache.openjpa.enhance;
+
+import java.io.Externalizable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.openjpa.lib.util.StringUtil;
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.meta.ClassArgParser;
+import org.apache.openjpa.lib.util.BytecodeWriter;
+import org.apache.openjpa.lib.util.ClassUtil;
+import org.apache.openjpa.lib.util.Files;
+import org.apache.openjpa.lib.util.J2DoPrivHelper;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.Services;
+import org.apache.openjpa.lib.util.Localizer.Message;
+import org.apache.openjpa.lib.util.svn.SVNUtils;
+import org.apache.openjpa.meta.AccessCode;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.meta.ValueStrategies;
+import org.apache.openjpa.util.ApplicationIds;
+import org.apache.openjpa.util.GeneralException;
+import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.BigDecimalId;
+import org.apache.openjpa.util.BigIntegerId;
+import org.apache.openjpa.util.ByteId;
+import org.apache.openjpa.util.CharId;
+import org.apache.openjpa.util.DateId;
+import org.apache.openjpa.util.DoubleId;
+import org.apache.openjpa.util.Id;
+import org.apache.openjpa.util.IntId;
+import org.apache.openjpa.util.FloatId;
+import org.apache.openjpa.util.LongId;
+import org.apache.openjpa.util.ObjectId;
+import org.apache.openjpa.util.ShortId;
+import org.apache.openjpa.util.StringId;
+import org.apache.openjpa.util.OpenJPAException;
+import org.apache.openjpa.util.UserException;
+import org.apache.openjpa.util.ImplHelper;
+import serp.bytecode.BCClass;
+import serp.bytecode.BCField;
+import serp.bytecode.BCMethod;
+import serp.bytecode.Code;
+import serp.bytecode.Constants;
+import serp.bytecode.Exceptions;
+import serp.bytecode.FieldInstruction;
+import serp.bytecode.GetFieldInstruction;
+import serp.bytecode.IfInstruction;
+import serp.bytecode.Instruction;
+import serp.bytecode.JumpInstruction;
+import serp.bytecode.LoadInstruction;
+import serp.bytecode.LookupSwitchInstruction;
+import serp.bytecode.MethodInstruction;
+import serp.bytecode.Project;
+import serp.bytecode.PutFieldInstruction;
+import serp.bytecode.TableSwitchInstruction;
+import serp.bytecode.ClassInstruction;
+
+/**
+ * Bytecode enhancer used to enhance persistent classes from metadata. The
+ * enhancer must be invoked on all persistence-capable and persistence aware
+ * classes.
+ *
+ * @author Abe White
+ */
+public class PCEnhancerSerp {
+    // Designates a version for maintaining compatbility when PCEnhancer
+    // modifies enhancement that can break serialization or other contracts
+    // Each enhanced class will return the value of this field via
+    // public int getEnhancementContractVersion()
+    public static final int ENHANCER_VERSION;
+
+    boolean _addVersionInitFlag = true;
+
+    public static final String PRE = "pc";
+    public static final String ISDETACHEDSTATEDEFINITIVE = PRE 
+        + "isDetachedStateDefinitive";
+
+    private static final Class PCTYPE = PersistenceCapable.class;
+    private static final String SM = PRE + "StateManager";
+    private static final Class SMTYPE = StateManager.class;
+    private static final String INHERIT = PRE + "InheritedFieldCount";
+    private static final String CONTEXTNAME = "GenericContext";
+    private static final Class USEREXCEP = UserException.class;
+    private static final Class INTERNEXCEP = InternalException.class;
+    private static final Class HELPERTYPE = PCRegistry.class;
+    private static final String SUPER = PRE + "PCSuperclass";
+    private static final Class OIDFSTYPE = FieldSupplier.class;
+    private static final Class OIDFCTYPE = FieldConsumer.class;
+    
+    private static final String VERSION_INIT_STR =  PRE + "VersionInit";
+
+    private static final Localizer _loc = Localizer.forPackage(PCEnhancer.class);
+    private static final String REDEFINED_ATTRIBUTE = PCEnhancer.class.getName() + "#redefined-type";
+    
+    private static final AuxiliarySerpEnhancer[] _auxEnhancers;
+    static {
+        Class[] classes = Services.getImplementorClasses(
+            AuxiliarySerpEnhancer.class,
+            AccessController.doPrivileged(
+                J2DoPrivHelper.getClassLoaderAction(AuxiliarySerpEnhancer.class)));
+        List auxEnhancers = new ArrayList(classes.length);
+        for (int i = 0; i < classes.length; i++) {
+            try {
+                auxEnhancers.add(AccessController.doPrivileged(
+                    J2DoPrivHelper.newInstanceAction(classes[i])));
+            } catch (Throwable t) {
+                // aux enhancer may rely on non-existant spec classes, etc
+            }
+        }
+        _auxEnhancers = (AuxiliarySerpEnhancer[]) auxEnhancers.toArray
+                (new AuxiliarySerpEnhancer[auxEnhancers.size()]);
+        
+        int rev = 0;
+        Properties revisionProps = new Properties();
+        try {
+            InputStream in = PCEnhancerSerp.class.getResourceAsStream("/META-INF/org.apache.openjpa.revision.properties");
+            if (in != null) {
+                try {
+                    revisionProps.load(in);
+                } finally {
+                    in.close();
+                }
+            }
+            String prop = revisionProps.getProperty("openjpa.enhancer.revision");
+            rev = SVNUtils.svnInfoToInteger(prop);
+        } catch (Exception e) {
+        }
+        if (rev > 0) {
+            ENHANCER_VERSION = rev;
+        } else {
+            // Something bad happened and we couldn't load from the properties file. We need to default to using the
+            // value of 2 because that is the value that was the value as of rev.511998.
+            // As of OpenJPA-3.0.0 we switched to '3' as we are now using ASM to generate the bytecode
+            ENHANCER_VERSION = 3;
+        }
+    }
+
+    private BCClass _pc;
+    private final BCClass _managedType;
+    private final MetaDataRepository _repos;
+    private final ClassMetaData _meta;
+    private final Log _log;
+    private Collection _oids = null;
+    private boolean _defCons = true;
+    private boolean _redefine = false;
+    private boolean _subclass = false;
+    private boolean _fail = false;
+    private Set _violations = null;
+    private File _dir = null;
+    private BytecodeWriter _writer = null;
+    private Map _backingFields = null; // map of set / get names => field names
+    private Map _attrsToFields = null; // map of attr names => field names
+    private Map _fieldsToAttrs = null; // map of field names => attr names
+    private boolean _isAlreadyRedefined = false;
+    private boolean _isAlreadySubclassed = false;
+    private boolean _bcsConfigured = false;
+
+    private boolean _optimizeIdCopy = false; // whether to attempt optimizing id copy
+    
+    /**
+     * Constructor. Supply configuration and type to enhance. This will look
+     * up the metadata for <code>type</code> from <code>conf</code>'s
+     * repository.
+     */
+    public PCEnhancerSerp(OpenJPAConfiguration conf, Class type) {
+        this(conf, AccessController.doPrivileged(J2DoPrivHelper
+            .loadProjectClassAction(new Project(), type)),
+            (MetaDataRepository) null);
+    }
+
+    /**
+     * Constructor. Supply configuration.
+     *
+     * @param type the bytecode representation fo the type to
+     * enhance; this can be created from any stream or file
+     * @param repos a metadata repository to use for metadata access,
+     * or null to create a new reporitory; the repository
+     * from the given configuration isn't used by default
+     * because the configuration might be an
+     * implementation-specific subclass whose metadata
+     * required more than just base metadata files
+     * @deprecated use {@link #PCEnhancerSerp(OpenJPAConfiguration, BCClass,
+        MetaDataRepository, ClassLoader)} instead. 
+     */
+    public PCEnhancerSerp(OpenJPAConfiguration conf, BCClass type, MetaDataRepository repos) {
+        this(conf, type, repos, null);
+    }
+
+    /**
+     * Constructor. Supply configuration.
+     *
+     * @param type the bytecode representation fo the type to
+     * enhance; this can be created from any stream or file
+     * @param repos a metadata repository to use for metadata access,
+     * or null to create a new reporitory; the repository
+     * from the given configuration isn't used by default
+     * because the configuration might be an
+     * implementation-specific subclass whose metadata
+     * required more than just base metadata files
+     * @param loader the environment classloader to use for loading
+     * classes and resources.
+     */
+    public PCEnhancerSerp(OpenJPAConfiguration conf, BCClass type, MetaDataRepository repos, ClassLoader loader) {
+        _managedType = type;
+        _pc = type;
+
+        _log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
+
+        if (repos == null) {
+            _repos = conf.newMetaDataRepositoryInstance();
+            _repos.setSourceMode(MetaDataRepository.MODE_META);
+        } else
+            _repos = repos;
+        _meta = _repos.getMetaData(type.getType(), loader, false);
+        
+        configureOptimizeIdCopy();
+    }
+
+    /**
+     * Constructor. Supply repository. The repository's configuration will
+     * be used, and the metadata passed in will be used as-is without doing
+     * any additional lookups. This is useful when running the enhancer
+     * during metadata load.
+     *
+     * @param repos a metadata repository to use for metadata access,
+     * or null to create a new reporitory; the repository
+     * from the given configuration isn't used by default
+     * because the configuration might be an
+     * implementation-specific subclass whose metadata
+     * required more than just base metadata files
+     * @param type the bytecode representation fo the type to
+     * enhance; this can be created from any stream or file
+     * @param meta the metadata to use for processing this type.
+     *
+     * @since 1.1.0
+     */
+    public PCEnhancerSerp(MetaDataRepository repos, BCClass type,
+                          ClassMetaData meta) {
+        _managedType = type;
+        _pc = type;
+
+        _log = repos.getConfiguration()
+            .getLog(OpenJPAConfiguration.LOG_ENHANCE);
+
+        _repos = repos;
+        _meta = meta;
+    }
+
+    static String toPCSubclassName(Class cls) {
+        return ClassUtil.getPackageName(PCEnhancerSerp.class) + "."
+            + cls.getName().replace('.', '$') + "$pcsubclass";
+    }
+
+    /**
+     * Whether or not <code>className</code> is the name for a
+     * dynamically-created persistence-capable subclass.
+     *
+     * @since 1.1.0
+     */
+    public static boolean isPCSubclassName(String className) {
+        return className.startsWith(ClassUtil.getPackageName(PCEnhancerSerp.class))
+            && className.endsWith("$pcsubclass");
+    }
+
+    /**
+     * If <code>className</code> is a dynamically-created persistence-capable
+     * subclass name, returns the name of the class that it subclasses.
+     * Otherwise, returns <code>className</code>.
+     *
+     * @since 1.1.0
+     */
+    public static String toManagedTypeName(String className) {
+        if (isPCSubclassName(className)) {
+            className = className.substring(
+                ClassUtil.getPackageName(PCEnhancerSerp.class).length() + 1);
+            className = className.substring(0, className.lastIndexOf("$"));
+            // this is not correct for nested PCs
+            className = className.replace('$', '.');
+        }
+        
+        return className;
+    }
+
+    /**
+     * Constructor. Supply configuration, type, and metadata.
+     */
+    public PCEnhancerSerp(OpenJPAConfiguration conf, BCClass type,
+                          ClassMetaData meta) {
+        this(conf, type, meta.getRepository());
+    }
+
+    /**
+     * Return the bytecode representation of the persistence-capable class
+     * being manipulated.
+     */
+    public BCClass getPCBytecode() {
+        return _pc;
+    }
+
+    /**
+     * Return the bytecode representation of the managed class being
+     * manipulated. This is usually the same as {@link #getPCBytecode},
+     * except when running the enhancer to redefine and subclass
+     * existing persistent types.
+     */
+    public BCClass getManagedTypeBytecode() {
+        return _managedType;
+    }
+
+    /**
+     * Return the metadata for the class being manipulated, or null if not
+     * a persistent type.
+     */
+    public ClassMetaData getMetaData() {
+        return _meta;
+    }
+
+    /**
+     * A boolean indicating whether the enhancer should add a no-args
+     * constructor if one is not already present in the class. OpenJPA
+     * requires that a no-arg constructor (whether created by the compiler
+     * or by the user) be present in a PC.
+     */
+    public boolean getAddDefaultConstructor() {
+        return _defCons;
+    }
+
+    /**
+     * A boolean indicating whether the enhancer should add a no-args
+     * constructor if one is not already present in the class. OpenJPA
+     * requires that a no-arg constructor (whether created by the compiler
+     * or by the user) be present in a PC.
+     */
+    public void setAddDefaultConstructor(boolean addDefaultConstructor) {
+        _defCons = addDefaultConstructor;
+    }
+
+    /**
+     * Whether the enhancer should mutate its arguments, or just run validation
+     * and optional subclassing logic on them. Usually used in conjunction with
+     * <code>setCreateSubclass(true)</code>.
+     *
+     * @since 1.0.0
+     */
+    public boolean getRedefine() {
+        return _redefine;
+    }
+
+    /**
+     * Whether the enhancer should mutate its arguments, or just run validation
+     * and optional subclassing logic on them. Usually used in conjunction with
+     * <code>setCreateSubclass(true)</code>.
+     *
+     * @since 1.0.0
+     */
+    public void setRedefine(boolean redefine) {
+        _redefine = redefine;
+    }
+
+    /**
+     * Whether the type that this instance is enhancing has already been
+     * redefined.
+     *
+     * @since 1.0.0
+     */
+    public boolean isAlreadyRedefined() {
+        return _isAlreadyRedefined;
+    }
+
+    /**
+     * Whether the type that this instance is enhancing has already been
+     * subclassed in this instance's environment classloader.
+     *
+     * @since 1.0.0
+     */
+    public boolean isAlreadySubclassed() {
+        return _isAlreadySubclassed;
+    }
+
+    /**
+     * Whether the enhancer should make its arguments persistence-capable,
+     * or generate a persistence-capable subclass.
+     *
+     * @since 1.0.0
+     */
+    public boolean getCreateSubclass() {
+        return _subclass;
+    }
+
+    /**
+     * Whether the enhancer should make its arguments persistence-capable,
+     * or generate a persistence-capable subclass.
+     *
+     * @since 1.0.0
+     */
+    public void setCreateSubclass(boolean subclass) {
+        _subclass = subclass;
+        _addVersionInitFlag = false;
+    }
+
+    /**
+     * Whether to fail if the persistent type uses property access and
+     * bytecode analysis shows that it may be violating OpenJPA's property
+     * access restrictions.
+     */
+    public boolean getEnforcePropertyRestrictions() {
+        return _fail;
+    }
+
+    /**
+     * Whether to fail if the persistent type uses property access and
+     * bytecode analysis shows that it may be violating OpenJPA's property
+     * access restrictions.
+     */
+    public void setEnforcePropertyRestrictions(boolean fail) {
+        _fail = fail;
+    }
+
+    /**
+     * The base build directory to generate code to. The proper package
+     * structure will be created beneath this directory. Defaults to
+     * overwriting the existing class file if null.
+     */
+    public File getDirectory() {
+        return _dir;
+    }
+
+    /**
+     * The base build directory to generate code to. The proper package
+     * structure will be creaed beneath this directory. Defaults to
+     * overwriting the existing class file if null.
+     */
+    public void setDirectory(File dir) {
+        _dir = dir;
+    }
+
+    /**
+     * Set the {@link BytecodeWriter} to write the bytecode to or null if none.
+     */
+    public void setBytecodeWriter(BytecodeWriter writer) {
+        _writer = writer;
+    }
+
+    /**
+     * Perform bytecode enhancements.
+     *
+     * @return <code>ENHANCE_*</code> constant
+     */
+    public int run() {
+        Class<?> type = _managedType.getType();
+        try {
+            // if managed interface, skip
+            if (_pc.isInterface())
+                return PCEnhancer.ENHANCE_INTERFACE;
+
+            // check if already enhanced
+            ClassLoader loader = AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(type));
+            for (String iface : _managedType.getDeclaredInterfaceNames()) {
+                if (iface.equals(PCTYPE.getName())) {
+                    if (_log.isTraceEnabled()) {
+                        _log.trace(_loc.get("pc-type", type, loader));
+                    }
+                    return PCEnhancer.ENHANCE_NONE;
+                }
+            }
+            if (_log.isTraceEnabled()) {
+                _log.trace(_loc.get("enhance-start", type, loader));
+            }
+
+
+            configureBCs();
+
+            // validate properties before replacing field access so that
+            // we build up a record of backing fields, etc
+            if (isPropertyAccess(_meta)) {
+                validateProperties();
+                if (getCreateSubclass())
+                    addAttributeTranslation();
+            }
+            replaceAndValidateFieldAccess();
+            processViolations();
+
+            if (_meta != null) {
+                enhanceClass();
+                addFields();
+                addStaticInitializer();
+                addPCMethods();
+                addAccessors();
+                addAttachDetachCode();
+                addSerializationCode();
+                addCloningCode();
+                runAuxiliaryEnhancers();
+                return PCEnhancer.ENHANCE_PC;
+            }
+            return PCEnhancer.ENHANCE_AWARE;
+        } catch (OpenJPAException ke) {
+            throw ke;
+        } catch (Exception e) {
+            throw new GeneralException(_loc.get("enhance-error",
+                type.getName(), e.getMessage()), e);
+        }
+    }
+
+    private void configureBCs() {
+        if (!_bcsConfigured) {
+            if (getRedefine()) {
+                if (_managedType.getAttribute(REDEFINED_ATTRIBUTE) == null)
+                    _managedType.addAttribute(REDEFINED_ATTRIBUTE);
+                else
+                    _isAlreadyRedefined = true;
+            }
+
+            if (getCreateSubclass()) {
+                PCSubclassValidator val = new PCSubclassValidator(
+                    _meta, _managedType, _log, _fail);
+                val.assertCanSubclass();
+
+                _pc = _managedType.getProject().loadClass(
+                    toPCSubclassName(_managedType.getType()));
+                if (_pc.getSuperclassBC() != _managedType) {
+                    _pc.setSuperclass(_managedType);
+                    _pc.setAbstract(_managedType.isAbstract());
+                    _pc.declareInterface(DynamicPersistenceCapable.class);
+                } else {
+                    _isAlreadySubclassed = true;
+                }
+            }
+
+            _bcsConfigured = true;
+        }
+    }
+
+    /**
+     * Write the generated bytecode.
+     */
+    public void record()
+        throws IOException {
+        if (_managedType != _pc && getRedefine())
+            record(_managedType);
+        record(_pc);
+        if (_oids != null)
+            for (Iterator itr = _oids.iterator(); itr.hasNext();)
+                record((BCClass) itr.next());
+    }
+
+    /**
+     * Write the given class.
+     */
+    private void record(BCClass bc)
+        throws IOException {
+        if (_writer != null)
+            _writer.write(bc);
+        else if (_dir == null)
+            AsmAdaptor.write(bc);
+        else {
+            File dir = Files.getPackageFile(_dir, bc.getPackageName(), true);
+            AsmAdaptor.write(bc, new File(dir, bc.getClassName() + ".class"));
+        }
+    }
+
+    /**
+     * Validate that the methods that use a property-access instance are
+     * written correctly. This method also gathers information on each
+     * property's backing field.
+     */
+    private void validateProperties() {
+        FieldMetaData[] fmds;
+        if (getCreateSubclass())
+            fmds = _meta.getFields();
+        else
+            fmds = _meta.getDeclaredFields();
+        Method meth;
+        BCMethod getter, setter;
+        BCField returned, assigned = null;
+        for (int i = 0; i < fmds.length; i++) {
+
+            if (!(fmds[i].getBackingMember() instanceof Method) ) {
+                // If not mixed access is not defined, flag the field members, 
+                // otherwise do not process them because they are valid
+                // persistent attributes.
+                if (!_meta.isMixedAccess()) {
+                    addViolation("property-bad-member",
+                        new Object[]{ fmds[i], fmds[i].getBackingMember() },
+                        true);
+                }
+                continue;
+            }
+            
+            meth = (Method) fmds[i].getBackingMember();
+            // ##### this will fail if we override and don't call super.
+            BCClass declaringType = _managedType.getProject()
+                .loadClass(fmds[i].getDeclaringType());
+            getter = declaringType.getDeclaredMethod(meth.getName(),
+                meth.getParameterTypes());
+            if (getter == null) {
+                addViolation("property-no-getter", new Object[]{ fmds[i] },
+                    true);
+                continue;
+            }
+            returned = getReturnedField(getter);
+            if (returned != null)
+                registerBackingFieldInfo(fmds[i], getter, returned);
+
+            setter = declaringType.getDeclaredMethod(getSetterName(fmds[i]),
+                new Class[]{ fmds[i].getDeclaredType() });
+            if (setter == null) {
+                if (returned == null) {
+                    addViolation("property-no-setter",
+                        new Object[]{ fmds[i] }, true);
+                    continue;
+                } else if (!getRedefine()) {
+                    // create synthetic setter
+                    setter = _managedType.declareMethod(getSetterName(fmds[i]),
+                        void.class, new Class[]{ fmds[i].getDeclaredType() });
+                    setter.makePrivate();
+                    Code code = setter.getCode(true);
+                    code.aload().setThis();
+                    code.xload().setParam(0);
+                    code.putfield().setField(returned);
+                    code.vreturn();
+                    code.calculateMaxStack();
+                    code.calculateMaxLocals();
+                }
+            }
+
+            if (setter != null)
+                assigned = getAssignedField(setter);
+
+            if (assigned != null) {
+                if (setter != null)
+                    registerBackingFieldInfo(fmds[i], setter, assigned);
+
+                if (assigned != returned)
+                    addViolation("property-setter-getter-mismatch", new Object[]
+                        { fmds[i], assigned.getName(), (returned == null) 
+                        ? null : returned.getName() }, false);
+            }
+        }
+    }
+
+    private void registerBackingFieldInfo(FieldMetaData fmd, BCMethod method,
+        BCField field) {
+        if (_backingFields == null)
+            _backingFields = new HashMap();
+        _backingFields.put(method.getName(), field.getName());
+
+        if (_attrsToFields == null)
+            _attrsToFields = new HashMap();
+        _attrsToFields.put(fmd.getName(), field.getName());
+
+        if (_fieldsToAttrs == null)
+            _fieldsToAttrs = new HashMap();
+        _fieldsToAttrs.put(field.getName(), fmd.getName());
+    }
+
+    private void addAttributeTranslation() {
+
+        // Get all field metadata
+        ArrayList<Integer> propFmds = new ArrayList<Integer>();
+        FieldMetaData[] fmds = _meta.getFields();
+
+        if (_meta.isMixedAccess()) {
+            // Stores indexes of property access fields to be used in
+            // 
+            propFmds = new ArrayList<Integer>();
+            
+            // Determine which fields have property access and save their 
+            // indexes
+            for (int i = 0; i < fmds.length; i++) {
+                if (isPropertyAccess(fmds[i]))
+                    propFmds.add(i);
+            }
+
+            // if no fields have property access do not do attribute translation
+            if (propFmds.size() == 0)
+                return;
+        }
+
+        _pc.declareInterface(AttributeTranslator.class);
+        BCMethod method = _pc.declareMethod(PRE + "AttributeIndexToFieldName",
+            String.class, new Class[] { int.class });
+        method.makePublic();
+        Code code = method.getCode(true);
+
+        // switch (val)
+        code.iload().setParam(0);
+        if (!_meta.isMixedAccess()) {
+            // if not mixed access use a table switch on all property-based fmd.
+            // a table switch is more efficient with +1 incremental operations
+            TableSwitchInstruction tabins = code.tableswitch();
+            
+            tabins.setLow(0);
+            tabins.setHigh(fmds.length - 1);
+
+            // case i:
+            //     return <_attrsToFields.get(fmds[i].getName())>
+            for (int i = 0; i < fmds.length; i++) {
+                tabins.addTarget(code.constant().setValue(
+                    _attrsToFields.get(fmds[i].getName())));
+                code.areturn();
+            }            
+            // default: throw new IllegalArgumentException ()
+            tabins.setDefaultTarget(throwException
+                (code, IllegalArgumentException.class));
+        }
+        else {
+            // In mixed access mode, property indexes are not +1 incremental 
+            // a lookup switch must be used to do indexed lookup.
+            LookupSwitchInstruction lookupins = code.lookupswitch();
+            
+            for (Integer i : propFmds) {
+                lookupins.addCase(i,
+                    code.constant().setValue(
+                    _attrsToFields.get(fmds[i].getName())));
+                code.areturn();
+            }            
+            // default: throw new IllegalArgumentException ()
+            lookupins.setDefaultTarget(throwException
+                (code, IllegalArgumentException.class));
+        }
+        
+        code.calculateMaxLocals();
+        code.calculateMaxStack();
+    }
+
+    /**
+     * Return the name of the setter method for the given field.
+     */
+    private static String getSetterName(FieldMetaData fmd) {
+        return fmd.getSetterName();
+    }
+
+    /**
+     * Return the field returned by the given method, or null if none.
+     * Package-protected and static for testing.
+     */
+    static BCField getReturnedField(BCMethod meth) {
+        return findField(meth, (AccessController.doPrivileged(
+            J2DoPrivHelper.newCodeAction())).xreturn()
+            .setType(meth.getReturnType()), false);
+    }
+
+    /**
+     * Return the field assigned in the given method, or null if none.
+     * Package-protected and static for testing.
+     */
+    static BCField getAssignedField(BCMethod meth) {
+        return findField(meth, (AccessController.doPrivileged(
+            J2DoPrivHelper.newCodeAction())).putfield(), true);
+    }
+
+    /**
+     * Return the field returned / assigned by <code>meth</code>. Returns
+     * null if non-fields (methods, literals, parameters, variables) are
+     * returned, or if non-parameters are assigned to fields.
+     */
+    private static BCField findField(BCMethod meth, Instruction template,
+        boolean findAccessed) {
+        // ignore any static methods. OpenJPA only currently supports
+        // non-static setters and getters
+        if (meth.isStatic())
+            return null;
+
+        Code code = meth.getCode(false);
+        if (code == null)
+            return null;
+        code.beforeFirst();
+
+        BCField field = null, cur;
+        Instruction templateIns, prevIns, earlierIns;
+        while (code.searchForward(template)) {
+            int backupCount = 3;
+            templateIns = code.previous();
+            if (!code.hasPrevious())
+                return null;
+            prevIns = code.previous();
+
+            if (prevIns instanceof ClassInstruction
+                && code.hasPrevious()) {
+                prevIns = code.previous();
+                backupCount++;
+            }
+
+            if (!code.hasPrevious())
+                return null;
+            earlierIns = code.previous();
+
+            // if the opcode two before the template was an aload_0, check
+            // against the middle instruction based on what type of find
+            // we're doing
+            if (!(earlierIns instanceof LoadInstruction)
+                || !((LoadInstruction) earlierIns).isThis())
+                return null;
+
+            // if the middle instruction was a getfield, then it's the
+            // field that's being accessed
+            if (!findAccessed && prevIns instanceof GetFieldInstruction) {
+                final FieldInstruction fPrevIns = (FieldInstruction) prevIns;
+                cur = AccessController.doPrivileged(
+                    J2DoPrivHelper.getFieldInstructionFieldAction(fPrevIns));
+                // if the middle instruction was an xload_1, then the
+                // matched instruction is the field that's being set.
+            } else if (findAccessed && prevIns instanceof LoadInstruction
+                && ((LoadInstruction) prevIns).getParam() == 0) {
+                final FieldInstruction fTemplateIns =
+                    (FieldInstruction) templateIns;
+                cur = AccessController.doPrivileged(J2DoPrivHelper
+                    .getFieldInstructionFieldAction(fTemplateIns));
+            } else
+                return null;
+
+            if (field != null && cur != field)
+                return null;
+            field = cur;
+
+            // ready for next search iteration
+            while (backupCount > 0) {
+                code.next();
+                backupCount--;
+            }
+        }
+        return field;
+    }
+
+    /**
+     * Record a violation of the property access restrictions.
+     */
+    private void addViolation(String key, Object[] args, boolean fatal) {
+        if (_violations == null)
+            _violations = new HashSet();
+        _violations.add(_loc.get(key, args));
+        _fail |= fatal;
+    }
+
+    /**
+     * Log / throw recorded property access violations.
+     */
+    private void processViolations() {
+        if (_violations == null)
+            return;
+
+        String sep = J2DoPrivHelper.getLineSeparator();
+        StringBuilder buf = new StringBuilder();
+        for (Iterator itr = _violations.iterator(); itr.hasNext();) {
+            buf.append(itr.next());
+            if (itr.hasNext())
+                buf.append(sep);
+        }
+        Message msg = _loc.get("property-violations", buf);
+
+        if (_fail)
+            throw new UserException(msg);
+        if (_log.isWarnEnabled())
+            _log.warn(msg);
+    }
+
+    /**
+     * Replaced all direct access to managed fields with the appropriate
+     * pcGet/pcSet method. Note that this includes access to fields
+     * owned by PersistenceCapable classes other than this one.
+     */
+    private void replaceAndValidateFieldAccess() throws NoSuchMethodException {
+        // create template putfield/getfield instructions to search for
+        Code template = AccessController.doPrivileged(
+            J2DoPrivHelper.newCodeAction());
+        Instruction put = template.putfield();
+        Instruction get = template.getfield();
+        Instruction stat = template.invokestatic();
+
+        // look through all methods; this is done before any methods are added
+        // so we don't need to worry about excluding synthetic methods.
+        BCMethod[] methods = _managedType.getDeclaredMethods();
+        Code code;
+        for (int i = 0; i < methods.length; i++) {
+            code = methods[i].getCode(false);
+
+            // don't modify the methods specified by the auxiliary enhancers
+            if (code != null && !skipEnhance(methods[i])) {
+                replaceAndValidateFieldAccess(code, get, true, stat);
+                replaceAndValidateFieldAccess(code, put, false, stat);
+            }
+        }
+    }
+
+    /**
+     * Replaces all instructions matching the given template in the given
+     * code block with calls to the appropriate generated getter/setter.
+     *
+     * @param code the code block to modify; the code iterator will
+     * be placed before the first instruction on method start,
+     * and will be after the last instruction on method completion
+     * @param ins the template instruction to search for; either a
+     * getfield or putfield instruction
+     * @param get boolean indicating if this is a get instruction
+     * @param stat template invokestatic instruction to replace with
+     */
+    private void replaceAndValidateFieldAccess(Code code, Instruction ins,
+        boolean get, Instruction stat) throws NoSuchMethodException {
+        code.beforeFirst();
+
+        FieldInstruction fi;
+        MethodInstruction mi;
+        ClassMetaData owner;
+        String name, typeName, methodName;
+        while (code.searchForward(ins)) {
+            // back up to the matched instruction
+            fi = (FieldInstruction) code.previous();
+            name = fi.getFieldName();
+            typeName = fi.getFieldTypeName();
+            owner = getPersistenceCapableOwner(name, fi.getFieldDeclarerType());
+            FieldMetaData fmd = owner == null ? null : owner.getField(name);
+            if (isPropertyAccess(fmd)) {
+                // if we're directly accessing a field in another class
+                // hierarchy that uses property access, something is wrong
+                if (owner != _meta && owner.getDeclaredField(name) != null &&
+                    _meta != null && !owner.getDescribedType()
+                        .isAssignableFrom(_meta.getDescribedType()))
+                    throw new UserException(_loc.get("property-field-access",
+                        new Object[]{ _meta, owner, name,
+                            code.getMethod().getName() }));
+
+                // if we're directly accessing a property-backing field outside
+                // the property in our own class, notify user
+                if (isBackingFieldOfAnotherProperty(name, code))
+                    addViolation("property-field-access", new Object[]{ _meta,
+                        owner, name, code.getMethod().getName() }, false);
+            }
+
+            if (owner == null ||
+                owner.getDeclaredField(fromBackingFieldName(name)) == null) {
+                // not persistent field?
+                code.next();
+                continue;
+            } else if (!getRedefine() && !getCreateSubclass()
+                && isFieldAccess(fmd)) {
+                // replace the instruction with a call to the generated access
+                // method
+                mi = (MethodInstruction) code.set(stat);
+
+                // invoke the proper access method, whether getter or setter
+                String prefix = (get) ? PRE + "Get" : PRE + "Set";
+                methodName = prefix + name;
+                if (get) {
+                    mi.setMethod(getType(owner).getName(),
+                        methodName, typeName, new String[]
+                        { getType(owner).getName() });
+                } else {
+                    mi.setMethod(getType(owner).getName(),
+                        methodName, "void", new String[]
+                        { getType(owner).getName(), typeName });
+                }
+                code.next();
+            } else if (getRedefine()) {
+                name = fromBackingFieldName(name);
+                if (get) {
+                    addNotifyAccess(code, owner.getField(name));
+                    code.next();
+                } else {
+                    // insert the set operations after the field mutation, but
+                    // first load the old value for use in the
+                    // StateManager.settingXXX method.
+                    loadManagedInstance(code, false);
+                    final FieldInstruction fFi = fi;
+                    code.getfield().setField(
+                        AccessController.doPrivileged(J2DoPrivHelper
+                            .getFieldInstructionFieldAction(fFi)));
+                    int val = code.getNextLocalsIndex();
+                    code.xstore().setLocal(val).setType(fi.getFieldType());
+
+                    // move past the putfield
+                    code.next();
+                    addNotifyMutation(code, owner.getField(name), val, -1);
+                }
+            } else {
+                code.next();
+            }
+            code.calculateMaxLocals();
+            code.calculateMaxStack();
+        }
+    }
+
+    private void addNotifyAccess(Code code, FieldMetaData fmd) {
+        // PCHelper.accessingField(this, <absolute-index>);
+        code.aload().setThis();
+        code.constant().setValue(fmd.getIndex());
+        code.invokestatic().setMethod(RedefinitionHelper.class,
+            "accessingField", void.class,
+            new Class[] { Object.class, int.class });
+    }
+
+    /**
+     * This must be called after setting the value in the object.
+     * 
+     * @param val the position in the local variable table where the
+     * old value is stored
+     * @param param the parameter position containing the new value, or
+     * -1 if the new value is unavailable and should therefore be looked
+     * up.
+     * @throws NoSuchMethodException
+     */
+    private void addNotifyMutation(Code code, FieldMetaData fmd, int val,
+        int param)
+        throws NoSuchMethodException {
+        // PCHelper.settingField(this, <absolute-index>, old, new);
+        code.aload().setThis();
+        code.constant().setValue(fmd.getIndex());
+        Class type = fmd.getDeclaredType();
+        // we only have special signatures for primitives and Strings
+        if (!type.isPrimitive() && type != String.class)
+            type = Object.class;
+        code.xload().setLocal(val).setType(type);
+        if (param == -1) {
+            loadManagedInstance(code, false);
+            addGetManagedValueCode(code, fmd);
+        } else {
+            code.xload().setParam(param).setType(type);
+        }
+        code.invokestatic().setMethod(RedefinitionHelper.class, "settingField",
+            void.class, new Class[] {
+                Object.class, int.class, type, type
+        });
+    }
+
+    /**
+     * Return true if the given instruction accesses a field that is a backing
+     * field of another property in this property-access class.
+     */
+    private boolean isBackingFieldOfAnotherProperty(String name, Code code) {
+        String methName = code.getMethod().getName();
+        return !"<init>".equals(methName)
+            && _backingFields != null
+            && !name.equals(_backingFields.get(methName))
+            && _backingFields.containsValue(name);
+    }
+
+    /**
+     * Helper method to return the declaring PersistenceCapable class of
+     * the given field.
+     *
+     * @param fieldName the name of the field
+     * @param owner the nominal owner of the field
+     * @return the metadata for the PersistenceCapable type that
+     * declares the field (and therefore has the static method), or null if none
+     */
+    private ClassMetaData getPersistenceCapableOwner(String fieldName,
+        Class owner) {
+        // find the actual ancestor class that declares the field, then
+        // check if the class is persistent, and if the field is managed
+        Field f = Reflection.findField(owner, fieldName, false);
+        if (f == null)
+            return null;
+
+        // managed interface
+        if (_meta != null && _meta.getDescribedType().isInterface())
+            return _meta;
+
+        return _repos.getMetaData(f.getDeclaringClass(), null, false);
+    }
+
+    /**
+     * Adds all synthetic methods to the bytecode by delegating to
+     * the various addXXXMethods () functions in this class. Includes
+     * all static field access methods.
+     * Note that the 'stock' methods like <code>pcIsTransactional</code>,
+     * <code>pcFetchObjectId</code>, etc are defined only in the
+     * least-derived PersistenceCapable type.
+     */
+    private void addPCMethods()
+        throws NoSuchMethodException {
+        addClearFieldsMethod();
+        addNewInstanceMethod(true);
+        addNewInstanceMethod(false);
+        addManagedFieldCountMethod();
+        addReplaceFieldsMethods();
+        addProvideFieldsMethods();
+        addCopyFieldsMethod();
+
+        if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
+            addStockMethods();
+            addGetVersionMethod();
+            addReplaceStateManagerMethod();
+
+            if (_meta.getIdentityType() != ClassMetaData.ID_APPLICATION)
+                addNoOpApplicationIdentityMethods();
+        }
+
+        // add the app id methods to each subclass rather
+        // than just the superclass, since it is possible to have
+        // a subclass with an app id hierarchy that matches the
+        // persistent class inheritance hierarchy
+        if (_meta.getIdentityType() == ClassMetaData.ID_APPLICATION
+            && (_meta.getPCSuperclass() == null || getCreateSubclass() ||
+                _meta.getObjectIdType() !=
+                    _meta.getPCSuperclassMetaData().getObjectIdType())) {
+            addCopyKeyFieldsToObjectIdMethod(true);
+            addCopyKeyFieldsToObjectIdMethod(false);
+            addCopyKeyFieldsFromObjectIdMethod(true);
+            addCopyKeyFieldsFromObjectIdMethod(false);
+            if (_meta.hasAbstractPKField() == true) { 
+                addGetIDOwningClass();
+            }
+            
+            if (_meta.isEmbeddable() && _meta.getIdentityType() == ClassMetaData.ID_APPLICATION) {
+                _log.warn(_loc.get("ID-field-in-embeddable-unsupported", _meta.toString()));
+            }
+            
+            addNewObjectIdInstanceMethod(true);
+            addNewObjectIdInstanceMethod(false);
+        }
+        else if (_meta.hasPKFieldsFromAbstractClass()){ 
+            addGetIDOwningClass();
+        }
+    }
+
+    /**
+     * Add a method to clear all persistent fields; we'll call this from
+     * the new instance method to ensure that unloaded fields have
+     * default values.
+     */
+    private void addClearFieldsMethod()
+        throws NoSuchMethodException {
+        // protected void pcClearFields ()
+        BCMethod method = _pc.declareMethod(PRE + "ClearFields", void.class,
+            null);
+        method.makeProtected();
+        Code code = method.getCode(true);
+
+        // super.pcClearFields ()
+        if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
+            code.aload().setThis();
+            code.invokespecial().setMethod(getType(_meta.
+                getPCSuperclassMetaData()), PRE + "ClearFields", void.class,
+                null);
+        }
+
+        FieldMetaData[] fmds = _meta.getDeclaredFields();
+        for (int i = 0; i < fmds.length; i++) {
+            if (fmds[i].getManagement() != FieldMetaData.MANAGE_PERSISTENT)
+                continue;
+
+            loadManagedInstance(code, false);
+            switch (fmds[i].getDeclaredTypeCode()) {
+                case JavaTypes.BOOLEAN:
+                case JavaTypes.BYTE:
+                case JavaTypes.CHAR:
+                case JavaTypes.INT:
+                case JavaTypes.SHORT:
+                    code.constant().setValue(0);
+                    break;
+                case JavaTypes.DOUBLE:
+                    code.constant().setValue(0D);
+                    break;
+                case JavaTypes.FLOAT:
+                    code.constant().setValue(0F);
+                    break;
+                case JavaTypes.LONG:
+                    code.constant().setValue(0L);
+                    break;
+                default:
+                    code.constant().setNull();
+                    break;
+            }
+
+            addSetManagedValueCode(code, fmds[i]);
+        }
+
+        code.vreturn();
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+    }
+
+    /**
+     * Adds the <code>pcNewInstance</code> method to the bytecode.
+     * These methods are used by the impl helper to create new
+     * managed instances efficiently without reflection.
+     *
+     * @param oid set to true to mimic the method version that takes
+     * an oid value as well as a state manager
+     */
+    private void addNewInstanceMethod(boolean oid) {
+        // public PersistenceCapable pcNewInstance (...)
+        Class[] args =
+            (oid) ? new Class[]{ SMTYPE, Object.class, boolean.class }
+                : new Class[]{ SMTYPE, boolean.class };
+        BCMethod method = _pc.declareMethod(PRE + "NewInstance", PCTYPE, args);
+        Code code = method.getCode(true);
+
+        // if the type is abstract, throw a UserException
+        if (_pc.isAbstract()) {
+            throwException(code, USEREXCEP);
+
+            code.calculateMaxStack();
+            code.calculateMaxLocals();
+            return;
+        }
+
+        // XXX pc = new XXX ();
+        code.anew().setType(_pc);
+        code.dup();
+        code.invokespecial().setMethod("<init>", void.class, null);
+        int inst = code.getNextLocalsIndex();
+        code.astore().setLocal(inst);
+
+        // if (clear)
+        //   pc.pcClearFields ();
+        code.iload().setParam((oid) ? 2 : 1);
+        JumpInstruction noclear = code.ifeq();
+        code.aload().setLocal(inst);
+        code.invokevirtual().setMethod(PRE + "ClearFields", void.class, null);
+
+        // pc.pcStateManager = sm;
+        noclear.setTarget(code.aload().setLocal(inst));
+        code.aload().setParam(0);
+        code.putfield().setField(SM, SMTYPE);
+
+        // copy key fields from oid
+        if (oid) {
+            code.aload().setLocal(inst);
+            code.aload().setParam(1);
+            code.invokevirtual().setMethod(PRE + "CopyKeyFieldsFromObjectId",
+                void.class, new Class[]{ Object.class });
+        }
+
+        // return pc;
+        code.aload().setLocal(inst);
+        code.areturn();
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+    }
+
+    /**
+     * Adds the <code>protected static int pcGetManagedFieldCount ()</code>
+     * method to the bytecode, returning the inherited field count added
+     * to the number of managed fields in the current PersistenceCapable class.
+     */
+    private void addManagedFieldCountMethod() {
+        // protected static int pcGetManagedFieldCount ()
+        BCMethod method = _pc.declareMethod(PRE + "GetManagedFieldCount",
+            int.class, null);
+        method.setStatic(true);
+        method.makeProtected();
+        Code code = method.getCode(true);
+
+        // return <fields> + pcInheritedFieldCount
+        // awhite: the above should work, but I'm seeing a messed up situation
+        // all of a sudden where when a subclass calls this method, it somehow
+        // happens before <clinit> is ever invoked, and so our
+        // pcInheritedFieldCount field isn't initialized!  so instead,
+        // return <fields> + <superclass>.pcGetManagedFieldCount ()
+        code.constant().setValue(_meta.getDeclaredFields().length);
+        if (_meta.getPCSuperclass() != null) {
+            Class superClass = getType(_meta.getPCSuperclassMetaData());
+            String superName = getCreateSubclass() ?
+                PCEnhancerSerp.toPCSubclassName(superClass) :
+                superClass.getName();
+            code.invokestatic().setMethod(superName,
+                PRE + "GetManagedFieldCount", int.class.getName(), null);
+            code.iadd();
+        }
+        code.ireturn();
+        code.calculateMaxStack();
+    }
+
+    /**
+     * Adds the {@link PersistenceCapable#pcProvideField} and
+     * {@link PersistenceCapable#pcProvideFields} methods to the bytecode.
+     */
+    private void addProvideFieldsMethods()
+        throws NoSuchMethodException {
+        // public void pcProvideField (int fieldNumber)
+        BCMethod method = _pc.declareMethod(PRE + "ProvideField", void.class,
+            new Class[]{ int.class });
+        Code code = method.getCode(true);
+
+        // adds everything through the switch ()
+        int relLocal = beginSwitchMethod(PRE + "ProvideField", code);
+
+        // if no fields in this inst, just throw exception
+        FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
+            : _meta.getDeclaredFields();
+        if (fmds.length == 0)
+            throwException(code, IllegalArgumentException.class);
+        else {
+            // switch (val)
+            code.iload().setLocal(relLocal);
+            TableSwitchInstruction tabins = code.tableswitch();
+            tabins.setLow(0);
+            tabins.setHigh(fmds.length - 1);
+
+            // <field> = pcStateManager.provided<type>Field
+            //     (this, fieldNumber);
+            for (int i = 0; i < fmds.length; i++) {
+                tabins.addTarget(loadManagedInstance(code, false));
+                code.getfield().setField(SM, SMTYPE);
+                loadManagedInstance(code, false);
+                code.iload().setParam(0);
+                loadManagedInstance(code, false);
+                addGetManagedValueCode(code, fmds[i]);
+                code.invokeinterface().setMethod(getStateManagerMethod
+                    (fmds[i].getDeclaredType(), "provided", false, false));
+                code.vreturn();
+            }
+
+            // default: throw new IllegalArgumentException ()
+            tabins.setDefaultTarget(throwException
+                (code, IllegalArgumentException.class));
+        }
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+
+        addMultipleFieldsMethodVersion(method);
+    }
+
+    /**
+     * Adds the {@link PersistenceCapable#pcReplaceField} and
+     * {@link PersistenceCapable#pcReplaceFields} methods to the bytecode.
+     */
+    private void addReplaceFieldsMethods()
+        throws NoSuchMethodException {
+        // public void pcReplaceField (int fieldNumber)
+        BCMethod method = _pc.declareMethod(PRE + "ReplaceField", void.class,
+            new Class[]{ int.class });
+        Code code = method.getCode(true);
+
+        // adds everything through the switch ()
+        int relLocal = beginSwitchMethod(PRE + "ReplaceField", code);
+
+        // if no fields in this inst, just throw exception
+        FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
+            : _meta.getDeclaredFields();
+        if (fmds.length == 0)
+            throwException(code, IllegalArgumentException.class);
+        else {
+            // switch (val)
+            code.iload().setLocal(relLocal);
+            TableSwitchInstruction tabins = code.tableswitch();
+            tabins.setLow(0);
+            tabins.setHigh(fmds.length - 1);
+
+            // <field> = pcStateManager.replace<type>Field
+            //  (this, fieldNumber);
+            for (int i = 0; i < fmds.length; i++) {
+                // for the addSetManagedValueCode call below.
+                tabins.addTarget(loadManagedInstance(code, false, fmds[i]));
+
+                loadManagedInstance(code, false, fmds[i]);
+                code.getfield().setField(SM, SMTYPE);
+                loadManagedInstance(code, false, fmds[i]);
+                code.iload().setParam(0);
+                code.invokeinterface().setMethod(getStateManagerMethod
+                    (fmds[i].getDeclaredType(), "replace", true, false));
+                if (!fmds[i].getDeclaredType().isPrimitive())
+                    code.checkcast().setType(fmds[i].getDeclaredType());
+
+                addSetManagedValueCode(code, fmds[i]);
+                if(_addVersionInitFlag){
+                    if(fmds[i].isVersion()){
+                        // If this case is setting the version field
+                        // pcVersionInit = true;
+                        loadManagedInstance(code, false);
+                        code.constant().setValue(1);
+                        putfield(code, null, VERSION_INIT_STR, boolean.class);
+                    }
+                }
+                code.vreturn();
+            }
+
+            // default: throw new IllegalArgumentException ()
+            tabins.setDefaultTarget(throwException
+                (code, IllegalArgumentException.class));
+        }
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+
+        addMultipleFieldsMethodVersion(method);
+    }
+
+    /**
+     * Adds the {@link PersistenceCapable#pcCopyFields} method to the bytecode.
+     */
+    private void addCopyFieldsMethod()
+        throws NoSuchMethodException {
+        // public void pcCopyField (Object pc, int field)
+        BCMethod method = _pc.declareMethod(PRE + "CopyField",
+            void.class.getName(),
+            new String[]{ _managedType.getName(), int.class.getName() });
+        method.makeProtected();
+        Code code = method.getCode(true);
+
+        // adds everything through the switch ()
+        int relLocal = beginSwitchMethod(PRE + "CopyField", code);
+
+        // if no fields in this inst, just throw exception
+        FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
+            : _meta.getDeclaredFields();
+        if (fmds.length == 0)
+            throwException(code, IllegalArgumentException.class);
+        else {
+            // switch (val)
+            code.iload().setLocal(relLocal);
+            TableSwitchInstruction tabins = code.tableswitch();
+            tabins.setLow(0);
+            tabins.setHigh(fmds.length - 1);
+
+            for (int i = 0; i < fmds.length; i++) {
+                // <field> = other.<field>;
+                // or set<field> (other.get<field>);
+                tabins.addTarget(loadManagedInstance(code, false, fmds[i]));
+                code.aload().setParam(0);
+                addGetManagedValueCode(code, fmds[i], false);
+                addSetManagedValueCode(code, fmds[i]);
+
+                // break;
+                code.vreturn();
+            }
+
+            // default: throw new IllegalArgumentException ()
+            tabins.setDefaultTarget(throwException
+                (code, IllegalArgumentException.class));
+        }
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+
+        addMultipleFieldsMethodVersion(method);
+    }
+
+    /**
+     * Helper method to add the code common to the beginning of both the
+     * pcReplaceField method and the pcProvideField method. This includes
+     * calculating the relative field number of the desired field and calling
+     * the superclass if necessary.
+     *
+     * @return the index in which the local variable holding the relative
+     * field number is stored
+     */
+    private int beginSwitchMethod(String name, Code code) {
+        boolean copy = (PRE + "CopyField").equals(name);
+        int fieldNumber = (copy) ? 1 : 0;
+
+        int relLocal = code.getNextLocalsIndex();
+        if (getCreateSubclass()) {
+            code.iload().setParam(fieldNumber);
+            code.istore().setLocal(relLocal);
+            return relLocal;
+        }
+
+        // int rel = fieldNumber - pcInheritedFieldCount
+        code.iload().setParam(fieldNumber);
+        code.getstatic().setField(INHERIT, int.class);
+        code.isub();
+        code.istore().setLocal(relLocal);
+        code.iload().setLocal(relLocal);
+
+        // super: if (rel < 0) super.pcReplaceField (fieldNumber); return;
+        // no super: if (rel < 0) throw new IllegalArgumentException ();
+        JumpInstruction ifins = code.ifge();
+        if (_meta.getPCSuperclass() != null) {
+            loadManagedInstance(code, false);
+            String[] args;
+            if (copy) {
+                args = new String[]{ getType(_meta.getPCSuperclassMetaData()).
+                    getName(), int.class.getName() };
+                code.aload().setParam(0);
+            } else
+                args = new String[]{ int.class.getName() };
+            code.iload().setParam(fieldNumber);
+            code.invokespecial().setMethod(getType(_meta.
+                getPCSuperclassMetaData()).getName(), name, 
+                void.class.getName(), args);
+            code.vreturn();
+        } else
+            throwException(code, IllegalArgumentException.class);
+
+        ifins.setTarget(code.nop());
+        return relLocal;
+    }
+
+    /**
+     * This helper method, given the pcReplaceField or pcProvideField
+     * method, adds the bytecode for the corresponding 'plural' version
+     * of the method -- the version that takes an int[] of fields to
+     * to access rather than a single field. The multiple fields version
+     * simply loops through the provided indexes and delegates to the
+     * singular version for each one.
+     */
+    private void addMultipleFieldsMethodVersion(BCMethod single) {
+        boolean copy = (PRE + "CopyField").equals(single.getName());
+
+        // public void <method>s (int[] fields)
+        Class[] args = (copy) ? new Class[]{ Object.class, int[].class }
+            : new Class[]{ int[].class };
+        BCMethod method = _pc.declareMethod(single.getName() + "s",
+            void.class, args);
+        Code code = method.getCode(true);
+
+        int fieldNumbers = 0;
+        int inst = 0;
+        if (copy) {
+            fieldNumbers = 1;
+
+            if (getCreateSubclass()) {
+                // get the managed instance into the local variable table
+                code.aload().setParam(0);
+                code.invokestatic().setMethod(ImplHelper.class,
+                    "getManagedInstance", Object.class,
+                    new Class[] { Object.class });
+                code.checkcast().setType(_managedType);
+                inst = code.getNextLocalsIndex();
+                code.astore().setLocal(inst);
+
+                // there might be a difference between the classes of 'this'
+                // vs 'other' in this context; use the PC methods to get the SM
+                code.aload().setParam(0);
+                code.aload().setThis();
+                code.getfield().setField(SM, SMTYPE);
+                code.invokestatic().setMethod(ImplHelper.class,
+                    "toPersistenceCapable", PersistenceCapable.class,
+                    new Class[] { Object.class, Object.class });
+                code.invokeinterface().setMethod(PersistenceCapable.class,
+                    "pcGetStateManager", StateManager.class, null);
+            } else {
+                // XXX other = (XXX) pc;
+                code.aload().setParam(0);
+                code.checkcast().setType(_pc);
+                inst = code.getNextLocalsIndex();
+                code.astore().setLocal(inst);
+
+                // access the other's sm field directly
+                code.aload().setLocal(inst);
+                code.getfield().setField(SM, SMTYPE);
+            }
+
+            // if (other.pcStateManager != pcStateManager)
+            //	throw new IllegalArgumentException
+
+            loadManagedInstance(code, false);
+            code.getfield().setField(SM, SMTYPE);
+            JumpInstruction ifins = code.ifacmpeq();
+            throwException(code, IllegalArgumentException.class);
+            ifins.setTarget(code.nop());
+
+            // if (pcStateManager == null)
+            //  throw new IllegalStateException
+            loadManagedInstance(code, false);
+            code.getfield().setField(SM, SMTYPE);
+            ifins = code.ifnonnull();
+            throwException(code, IllegalStateException.class);
+            ifins.setTarget(code.nop());
+        }
+
+        // for (int i = 0;
+        code.constant().setValue(0);
+        int idx = code.getNextLocalsIndex();
+        code.istore().setLocal(idx);
+        JumpInstruction testins = code.go2();
+
+        // <method> (fields[i]);
+        Instruction bodyins = loadManagedInstance(code, false);
+        if (copy)
+            code.aload().setLocal(inst);
+        code.aload().setParam(fieldNumbers);
+        code.iload().setLocal(idx);
+        code.iaload();
+        code.invokevirtual().setMethod(single);
+
+        // i++;
+        code.iinc().setIncrement(1).setLocal(idx);
+
+        // i < fields.length
+        testins.setTarget(code.iload().setLocal(idx));
+        code.aload().setParam(fieldNumbers);
+        code.arraylength();
+        code.ificmplt().setTarget(bodyins);
+        code.vreturn();
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+    }
+
+    /**
+     * Adds the 'stock' methods to the bytecode; these include methods
+     * like {@link PersistenceCapable#pcFetchObjectId}
+     * and {@link PersistenceCapable#pcIsTransactional}.
+     */
+    private void addStockMethods()
+        throws NoSuchMethodException {
+        try {
+            // pcGetGenericContext
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "get" + CONTEXTNAME, (Class[]) null)), false);
+    
+            // pcFetchObjectId
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "fetchObjectId", (Class[]) null)), false);
+    
+            // pcIsDeleted
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "isDeleted", (Class[]) null)), false);
+    
+            // pcIsDirty
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "isDirty", (Class[]) null)), true);
+    
+            // pcIsNew
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "isNew", (Class[]) null)), false);
+    
+            // pcIsPersistent
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "isPersistent", (Class[]) null)), false);
+    
+            // pcIsTransactional
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "isTransactional", (Class[]) null)), false);
+    
+            // pcSerializing
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "serializing", (Class[]) null)), false);
+    
+            // pcDirty
+            translateFromStateManagerMethod(
+                AccessController.doPrivileged(
+                    J2DoPrivHelper.getDeclaredMethodAction(
+                        SMTYPE, "dirty", new Class[]{ String.class })), false);
+    
+            // pcGetStateManager
+            BCMethod meth = _pc.declareMethod(PRE + "GetStateManager",
+                StateManager.class, null);
+            Code code = meth.getCode(true);
+            loadManagedInstance(code, false);
+            code.getfield().setField(SM, StateManager.class);
+            code.areturn();
+            code.calculateMaxStack();
+            code.calculateMaxLocals();
+        } catch (PrivilegedActionException pae) {
+             throw (NoSuchMethodException) pae.getException();
+        }
+    }
+
+    /**
+     * Helper method to add a stock method to the bytecode. Each
+     * stock method simply delegates to a corresponding StateManager method.
+     * Given the StateManager method, then, this function translates it into
+     * the wrapper method that should be added to the bytecode.
+     */
+    private void translateFromStateManagerMethod(Method m,
+        boolean isDirtyCheckMethod) {
+        // form the name of the method by prepending 'pc' to the sm method
+        String name = PRE + StringUtil.capitalize(m.getName());
+        Class[] params = m.getParameterTypes();
+        Class returnType = m.getReturnType();
+
+        // add the method to the pc
+        BCMethod method = _pc.declareMethod(name, returnType, params);
+        Code code = method.getCode(true);
+
+        // if (pcStateManager == null) return <default>;
+        loadManagedInstance(code, false);
+        code.getfield().setField(SM, SMTYPE);
+        JumpInstruction ifins = code.ifnonnull();
+        if (returnType.equals(boolean.class))
+            code.constant().setValue(false);
+        else if (!returnType.equals(void.class))
+            code.constant().setNull();
+        code.xreturn().setType(returnType);
+
+        // if this is the dirty-check method and we're subclassing but not
+        // redefining, hook into PCHelper to do the dirty check
+        if (isDirtyCheckMethod && !getRedefine()) {
+            // RedefinitionHelper.dirtyCheck(sm);
+            ifins.setTarget(loadManagedInstance(code, false));
+            code.getfield().setField(SM, SMTYPE);
+            code.dup(); // for the return statement below
+            code.invokestatic().setMethod(RedefinitionHelper.class, 
+                "dirtyCheck", void.class, new Class[] { SMTYPE });
+        } else {
+            ifins.setTarget(loadManagedInstance(code, false));
+            code.getfield().setField(SM, SMTYPE);
+        }
+        
+        // return pcStateManager.<method> (<args>);
+        // managed instance loaded above in if-else block
+        for (int i = 0; i < params.length; i++)
+            code.xload().setParam(i);
+        code.invokeinterface().setMethod(m);
+        code.xreturn().setType(returnType);
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+    }
+
+    /**
+     * Adds the {@link PersistenceCapable#pcGetVersion} method to the bytecode.
+     */
+    private void addGetVersionMethod()
+        throws NoSuchMethodException {
+        BCMethod method = _pc.declareMethod(PRE + "GetVersion", Object.class,
+            null);
+        Code code = method.getCode(true);
+
+        // if (pcStateManager == null)
+        loadManagedInstance(code, false);
+        code.getfield().setField(SM, SMTYPE);
+        JumpInstruction ifins = code.ifnonnull();
+        FieldMetaData versionField = _meta.getVersionField();
+
+        if (versionField == null)
+            code.constant().setNull(); // return null;
+        else {
+            // return <versionField>;
+            Class wrapper = toPrimitiveWrapper(versionField);
+            if (wrapper != versionField.getDeclaredType()) {
+                code.anew().setType(wrapper);
+                code.dup();
+            }
+            loadManagedInstance(code, false);
+            addGetManagedValueCode(code, versionField);
+            if (wrapper != versionField.getDeclaredType())
+                code.invokespecial().setMethod(wrapper, "<init>", void.class,
+                    new Class[]{ versionField.getDeclaredType() });
+        }
+        code.areturn();
+
+        // return pcStateManager.getVersion ();
+        ifins.setTarget(loadManagedInstance(code, false));
+        code.getfield().setField(SM, SMTYPE);
+        code.invokeinterface().setMethod(SMTYPE, "getVersion", Object.class,
+            null);
+        code.areturn();
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+    }
+
+    /**
+     * Return the version field type as a primitive wrapper, or null if
+     * the version field is not primitive.
+     */
+    private Class toPrimitiveWrapper(FieldMetaData fmd) {
+        switch (fmd.getDeclaredTypeCode()) {
+            case JavaTypes.BOOLEAN:
+                return Boolean.class;
+            case JavaTypes.BYTE:
+                return Byte.class;
+            case JavaTypes.CHAR:
+                return Character.class;
+            case JavaTypes.DOUBLE:
+                return Double.class;
+            case JavaTypes.FLOAT:
+                return Float.class;
+            case JavaTypes.INT:
+                return Integer.class;
+            case JavaTypes.LONG:
+                return Long.class;
+            case JavaTypes.SHORT:
+                return Short.class;
+        }
+        return fmd.getDeclaredType();
+    }
+
+    /**
+     * Adds the {@link PersistenceCapable#pcReplaceStateManager}
+     * method to the bytecode.
+     */
+    private void addReplaceStateManagerMethod() {
+        // public void pcReplaceStateManager (StateManager sm)
+        BCMethod method = _pc.declareMethod(PRE + "ReplaceStateManager",
+            void.class, new Class[]{ SMTYPE });
+        method.getExceptions(true).addException(SecurityException.class);
+        Code code = method.getCode(true);
+
+        // if (pcStateManager != null)
+        //	pcStateManager = pcStateManager.replaceStateManager(sm);
+        loadManagedInstance(code, false);
+        code.getfield().setField(SM, SMTYPE);
+        JumpInstruction ifins = code.ifnull();
+        loadManagedInstance(code, false);
+        loadManagedInstance(code, false);
+        code.getfield().setField(SM, SMTYPE);
+        code.aload().setParam(0);
+        code.invokeinterface().setMethod(SMTYPE, "replaceStateManager",
+            SMTYPE, new Class[]{ SMTYPE });
+        code.putfield().setField(SM, SMTYPE);
+        code.vreturn();
+
+        // SecurityManager sec = System.getSecurityManager ();
+        // if (sec != null)
+        //		sec.checkPermission (Permission.SET_STATE_MANAGER);
+        ifins.setTarget(code.invokestatic().setMethod(System.class,
+            "getSecurityManager", SecurityManager.class, null));
+
+        // pcStateManager = sm;
+        ifins.setTarget(loadManagedInstance(code, false));
+        code.aload().setParam(0);
+        code.putfield().setField(SM, SMTYPE);
+        code.vreturn();
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+    }
+
+    /**
+     * Creates the PersistenceCapable methods dealing with application
+     * identity and gives them no-op implementations.
+     */
+    private void addNoOpApplicationIdentityMethods() {
+        // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs,
+        // 	Object oid)
+        BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
+            void.class, new Class[]{ OIDFSTYPE, Object.class });
+        Code code = method.getCode(true);
+        code.vreturn();
+        code.calculateMaxLocals();
+
+        // public void pcCopyKeyFieldsToObjectId (Object oid)
+        method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
+            void.class, new Class[]{ Object.class });
+        code = method.getCode(true);
+        code.vreturn();
+        code.calculateMaxLocals();
+
+        // public void pcCopyKeyFieldsFromObjectId (ObjectIdFieldConsumer fc,
+        //	Object oid)
+        method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
+            void.class, new Class[]{ OIDFCTYPE, Object.class });
+        code = method.getCode(true);
+        code.vreturn();
+        code.calculateMaxLocals();
+
+        // public void pcCopyKeyFieldsFromObjectId (Object oid)
+        method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
+            void.class, new Class[]{ Object.class });
+        code = method.getCode(true);
+        code.vreturn();
+        code.calculateMaxLocals();
+
+        // public Object pcNewObjectIdInstance ()
+        method = _pc.declareMethod(PRE + "NewObjectIdInstance",
+            Object.class, null);
+        code = method.getCode(true);
+        code.constant().setNull();
+        code.areturn();
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+
+        // public Object pcNewObjectIdInstance (Object obj)
+        method = _pc.declareMethod(PRE + "NewObjectIdInstance",
+            Object.class, new Class[]{ Object.class });
+        code = method.getCode(true);
+        code.constant().setNull();
+        code.areturn();
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+    }
+
+    /**
+     * Adds the <code>pcCopyKeyFieldsToObjectId</code> methods
+     * to classes using application identity.
+     */
+    private void addCopyKeyFieldsToObjectIdMethod(boolean fieldManager)
+        throws NoSuchMethodException {
+        // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs,
+        //	Object oid)
+        String[] args = (fieldManager) ?
+            new String[]{ OIDFSTYPE.getName(), Object.class.getName() }
+            : new String[]{ Object.class.getName() };
+        BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
+            void.class.getName(), args);
+        Code code = method.getCode(true);
+
+        // single field identity always throws exception
+        if (_meta.isOpenJPAIdentity()) {
+            throwException(code, INTERNEXCEP);
+
+            code.calculateMaxStack();
+            code.calculateMaxLocals();
+            return;
+        }
+
+        // call superclass method
+        if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
+            loadManagedInstance(code, false);
+            for (int i = 0; i < args.length; i++)
+                code.aload().setParam(i);
+            code.invokespecial().setMethod(getType(_meta.
+                getPCSuperclassMetaData()).getName(),
+                PRE + "CopyKeyFieldsToObjectId", void.class.getName(), args);
+        }
+
+        // Object id = oid;
+        if (fieldManager)
+            code.aload().setParam(1);
+        else
+            code.aload().setParam(0);
+
+        if (_meta.isObjectIdTypeShared()) {
+            // oid = ((ObjectId) id).getId ();
+            code.checkcast().setType(ObjectId.class);
+            code.invokevirtual().setMethod(ObjectId.class, "getId",
+                Object.class, null);
+        }
+
+        // <oid type> id = (<oid type>) oid;
+        int id = code.getNextLocalsIndex();
+        Class oidType = _meta.getObjectIdType();
+        code.checkcast().setType(oidType);
+        code.astore().setLocal(id);
+
+        // int inherited = pcInheritedFieldCount;
+        int inherited = 0;
+        if (fieldManager) {
+            code.getstatic().setField(INHERIT, int.class);
+            inherited = code.getNextLocalsIndex();
+            code.istore().setLocal(inherited);
+        }
+
+        // id.<field> = fs.fetch<type>Field (<index>); or...
+        // id.<field> = pc.<field>;
+        FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
+            : _meta.getDeclaredFields();
+        Class<?> type; 
+        String name;
+        Field field;
+        Method setter;
+        boolean reflect;
+        // If optimizeIdCopy is enabled and not a field manager method, try to
+        // optimize the copyTo by using a public constructor instead of reflection
+        if (_optimizeIdCopy) {
+            ArrayList<Integer> pkfields = optimizeIdCopy(oidType, fmds);
+            if (pkfields != null) {
+                // search for a constructor on the IdClass that can be used
+                // to construct the IdClass
+                int parmOrder[] = getIdClassConstructorParmOrder(oidType, pkfields, fmds);
+                if (parmOrder != null) {
+                    // If using a field manager, values must be loaded into locals so they can be properly ordered
+                    // as constructor parameters.
+                    int[] localIndexes = new int[fmds.length];
+                    if (fieldManager) {
+                        for (int k = 0; k < fmds.length; k++) {
+                            if (!fmds[k].isPrimaryKey())
+                                continue;
+                            code.aload().setParam(0);
+                            code.constant().setValue(k);
+                            code.iload().setLocal(inherited);
+                            code.iadd();
+                            code.invokeinterface().setMethod(getFieldSupplierMethod(fmds[k].getObjectIdFieldType()));
+                            localIndexes[k] = code.getNextLocalsIndex();
+                            storeLocalValue(code, localIndexes[k], fmds[k].getObjectIdFieldTypeCode());
+                        }
+                    }
+                    
+                    // found a matching constructor.  parm array is constructor parm order
+                    code.anew().setType(oidType);
+                    code.dup();
+                    // build the parm list in order
+                    Class<?>[] clsArgs = new Class<?>[parmOrder.length];
+                    for (int i = 0; i < clsArgs.length; i++) {
+                        int parmIndex = parmOrder[i];
+                        clsArgs[i] = fmds[parmIndex].getObjectIdFieldType();
+                        if (!fieldManager) {
+                            loadManagedInstance(code, false);
+                            addGetManagedValueCode(code, fmds[parmIndex]);
+                        } else {
+                            // Load constructor parameters in appropriate order
+                            loadLocalValue(code, localIndexes[parmIndex], fmds[parmIndex].getObjectIdFieldTypeCode());
+                            if (fmds[parmIndex].getObjectIdFieldTypeCode() == JavaTypes.OBJECT &&
+                                !fmds[parmIndex].getDeclaredType().isEnum()) {
+                                code.checkcast().setType(ObjectId.class);
+                                code.invokevirtual().setMethod(ObjectId.class, "getId", 
+                                    Object.class, null);                    
+                            }
+                            // if the type of this field meta data is
+                            // non-primitive and non-string, be sure to cast
+                            // to the appropriate type.
+                            if (!clsArgs[i].isPrimitive()
+                                && !clsArgs[i].getName().equals(String.class.getName()))
+                                code.checkcast().setType(clsArgs[i]);
+                        }
+                    }
+                    // invoke the public constructor to create a new local id
+                    code.invokespecial().setMethod(oidType, "<init>", void.class, clsArgs);
+                    int ret = code.getNextLocalsIndex();
+                    code.astore().setLocal(ret);
+
+                    // swap out the app id with the new one
+                    code.aload().setLocal( fieldManager ? 2 : 1);
+                    code.checkcast().setType(ObjectId.class);
+                    code.aload().setLocal(ret);
+                    code.invokestatic().setMethod(ApplicationIds.class, 
+                            "setAppId", void.class, new Class[] { ObjectId.class,
+                            Object.class });
+                    code.vreturn();
+
+                    code.calculateMaxStack();
+                    code.calculateMaxLocals();
+                    return;
+                }
+            }
+        }
+        
+        for (int i = 0; i < fmds.length; i++) {
+            if (!fmds[i].isPrimaryKey())
+                continue;
+            code.aload().setLocal(id);
+
+            name = fmds[i].getName();
+            type = fmds[i].getObjectIdFieldType();
+            if (isFieldAccess(fmds[i])) {
+                setter = null;
+                field = Reflection.findField(oidType, name, true);
+                reflect = !Modifier.isPublic(field.getModifiers());
+                if (reflect) {
+                    code.classconstant().setClass(oidType);
+                    code.constant().setValue(name);
+                    code.constant().setValue(true);
+                    code.invokestatic().setMethod(Reflection.class, 
+                        "findField", Field.class, new Class[] { Class.class,
+                        String.class, boolean.class });
+                }
+            } else {
+                field = null;
+                setter = Reflection.findSetter(oidType, name, type, true);
+                reflect = !Modifier.isPublic(setter.getModifiers());
+                if (reflect) {
+                    code.classconstant().setClass(oidType);
+                    code.constant().setValue(name);
+                    code.classconstant().setClass(type);
+                    code.constant().setValue(true);
+                    code.invokestatic().setMethod(Reflection.class, 
+                        "findSetter", Method.class, new Class[] { Class.class,
+                        String.class, Class.class, boolean.class });
+                }
+            }
+
+            if (fieldManager) {
+                code.aload().setParam(0);
+                code.constant().setValue(i);
+                code.iload().setLocal(inherited);
+                code.iadd();
+                code.invokeinterface().setMethod
+                    (getFieldSupplierMethod(type));
+                if (fmds[i].getObjectIdFieldTypeCode() == JavaTypes.OBJECT &&
+                    !fmds[i].getDeclaredType().isEnum()) {
+                	code.checkcast().setType(ObjectId.class);
+                	code.invokevirtual().setMethod(ObjectId.class, "getId", 
+            			Object.class, null);                	
+                }
+
+                // if the type of this field meta data is
+                // non-primitive and non-string, be sure to cast
+                // to the appropriate type.
+                if (!reflect && !type.isPrimitive()
+                    && !type.getName().equals(String.class.getName()))
+                    code.checkcast().setType(type);
+            } else {
+                loadManagedInstance(code, false);
+                addGetManagedValueCode(code, fmds[i]);
+
+                // get id/pk from pc instance
+                if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC)
+                    addExtractObjectIdFieldValueCode(code, fmds[i]);
+            }
+
+            if (reflect && field != null) {
+                code.invokestatic().setMethod(Reflection.class, "set", 
+                    void.class, new Class[] { Object.class, Field.class,
+                    (type.isPrimitive()) ? type : Object.class });
+            } else if (reflect) { 
+                code.invokestatic().setMethod(Reflection.class, "set", 
+                    void.class, new Class[] { Object.class, Method.class,
+                    (type.isPrimitive()) ? type : Object.class });
+            } else if (field != null)
+                code.putfield().setField(field);
+            else
+                code.invokevirtual().setMethod(setter);
+        }
+        code.vreturn();
+
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+    }
+
+    /**
+     * Adds the appropriate load method for the given type and local
+     * index.
+     */
+    private void loadLocalValue(Code code, int locidx, int typeCode) {
+        switch (typeCode) {
+            case JavaTypes.CHAR:
+            case JavaTypes.BYTE:
+            case JavaTypes.SHORT:
+            case JavaTypes.INT:
+                code.iload().setLocal(locidx);
+                break;
+            case JavaTypes.DOUBLE:
+                code.dload().setLocal(locidx);
+                break;
+            case JavaTypes.FLOAT:
+                code.fload().setLocal(locidx);
+                break;
+            case JavaTypes.LONG:
+                code.lload().setLocal(locidx);
+                break;
+            default:
+                code.aload().setLocal(locidx);
+                break;
+        }
+    }
+
+    /**
+     * Adds the appropriate store method for the given type and local
+     * index.
+     */
+    private void storeLocalValue(Code code, int locidx, int typeCode) {
+        switch (typeCode) {
+            case JavaTypes.CHAR:
+            case JavaTypes.BYTE:
+            case JavaTypes.SHORT:
+            case JavaTypes.INT:
+                code.istore().setLocal(locidx);
+                break;
+            case JavaTypes.DOUBLE:
+                code.dstore().setLocal(locidx);
+                break;
+            case JavaTypes.FLOAT:
+                code.fstore().setLocal(locidx);
+                break;
+            case JavaTypes.LONG:
+                code.lstore().setLocal(locidx);
+                break;
+            default:
+                code.astore().setLocal(locidx);
+                break;
+        }
+    }
+
+    /**
+     * Add code to extract the id of the given primary key relation field for
+     * setting into an objectid instance.
+     */
+    private void addExtractObjectIdFieldValueCode(Code code, FieldMetaData pk) {
+        // if (val != null) 
+        //  val = ((PersistenceCapable) val).pcFetchObjectId();
+        int pc = code.getNextLocalsIndex();
+        code.astore().setLocal(pc);
+        code.aload().setLocal(pc);
+        JumpInstruction ifnull1 = code.ifnull();
+        code.aload().setLocal(pc);
+        code.checkcast().setType(PersistenceCapable.class); 
+        if (!pk.getTypeMetaData().isOpenJPAIdentity())
+            code.invokeinterface().setMethod(PersistenceCapable.class,
+                PRE + "FetchObjectId", Object.class, null);
+        else
+            code.invokeinterface().setMethod(PersistenceCapable.class,

[... 2727 lines stripped ...]