You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2005/05/22 20:01:48 UTC

svn commit: r171352 [9/11] - in /incubator/jdo/trunk/runtime20: ./ src/ src/conf/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/jdo/ src/java/org/apache/jdo/ejb/ src/java/org/apache/jdo/impl/ src/java/org/apache/jdo/impl/model/ src/java/org/apache/jdo/impl/model/java/ src/java/org/apache/jdo/impl/model/java/runtime/ src/java/org/apache/jdo/impl/model/jdo/ src/java/org/apache/jdo/impl/model/jdo/xml/ src/java/org/apache/jdo/impl/pm/ src/java/org/apache/jdo/impl/sco/ src/java/org/apache/jdo/impl/state/ src/java/org/apache/jdo/pm/ src/java/org/apache/jdo/query/ src/java/org/apache/jdo/sco/ src/java/org/apache/jdo/state/ src/java/org/apache/jdo/store/

Added: incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/state/StateManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/state/StateManagerImpl.java?rev=171352&view=auto
==============================================================================
--- incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/state/StateManagerImpl.java (added)
+++ incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/state/StateManagerImpl.java Sun May 22 11:01:45 2005
@@ -0,0 +1,2471 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * 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.
+ */
+
+/*
+ * StateManagerImpl.java
+ *
+ * Created on September 1, 2000, 2:29 PM
+ * @version 1.0.1
+ */
+
+package org.apache.jdo.impl.state;
+
+import java.util.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.jdo.*;
+
+import javax.jdo.spi.JDOImplHelper;
+import javax.jdo.spi.PersistenceCapable;
+import javax.jdo.spi.StateManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.jdo.impl.model.java.runtime.RuntimeJavaModelFactory;
+import org.apache.jdo.model.jdo.JDOClass;
+import org.apache.jdo.model.jdo.JDOField;
+import org.apache.jdo.pm.PersistenceManagerInternal;
+import org.apache.jdo.sco.SCO;
+import org.apache.jdo.sco.SCOCollection;
+import org.apache.jdo.sco.SCOMap;
+import org.apache.jdo.state.FieldManager;
+import org.apache.jdo.state.StateManagerInternal;
+import org.apache.jdo.store.StoreManager;
+import org.apache.jdo.util.I18NHelper;
+
+/**
+ * This is the StoreManager independent implemetation of
+ * javax.jdo.spi.StateManager interface. Delegates state transition
+ * requests to LifeCycleState.
+ *
+ * @author  Marina Vatkina
+ * @version 1.0.1
+ */
+class StateManagerImpl implements StateManagerInternal {
+
+    // Reference to the associated PersistenceManager as
+    // PersistenceManagerInternal
+    private PersistenceManagerInternal myPM = null;
+
+    // Current Transaction
+    private Transaction tx = null;
+
+    // Associated PersistenceCapable object
+    private PersistenceCapable myPC = null;
+
+    // LifeCycle state to handle state transition requests.
+    private LifeCycleState myLC = null;
+
+    private byte jdoFlags = 0;
+
+    /** beforeImage represents state of an instance before
+     * any change or as of the call to makePersistent/deletePersistent
+     */
+    private PersistenceCapable beforeImage = null;
+
+    /** flushedImage represents state of an instance as of the last flush
+     * to the datastore.
+     */
+    private PersistenceCapable flushedImage = null;
+
+    /** Helper StateFieldManager instance for resetting fields in a Hollow
+     * instance at commit/rollback.
+     */
+    private final static StateFieldManager hollowFieldManager =
+        new StateFieldManager();
+    
+    /** Helper StateFieldManager instance for fetching Object fields values for
+     * reachability and SCO processing.
+     */
+    private StateFieldManager objectFieldManager = new StateFieldManager();
+    
+    /** Reference to JDO Model.
+     */
+    private Object metaData = null;
+    private Class myPCClass = null;
+    
+    // Flag that indicates processing inside Transaction.afterCompletion when 
+    // fields are being reset to Java default values
+    private boolean inAfterCompletion = false;
+
+    // Flag that indicates state transition to Transient inside
+    // disconnect() method to allow setting jdoStateManager to null
+    // from call-back replacingStateManager()
+    private boolean transitionTransient = false;
+
+    // Flag that indicates that PersistenceManagerFactory supports option 
+    // javax.jdo.option.ChangeApplicationIdentity.
+    private boolean allowedChangeApplicationIdentity = false;
+
+    // Representation of the available ("get") fields
+    private BitSet loadedFields = null;
+
+    // Representation of the changed ("set") fields
+    private BitSet dirtyFields = null;
+
+    // Representation of the fieldspresent in the beforeImage
+    private BitSet biFields = null;
+
+    // Helper array to keep a single field number.
+    private int[] fieldArr = new int[1];
+
+    // The objectId of the object associated with this StateManager.
+    private Object objectId;
+
+    // The transactional objectId of the object associated with this
+    // StateManager.
+    private Object txObjectId;
+
+    // Object contains dependency information specific to this
+    // instance of the StateManager
+    private Object dependency = null;
+
+    // Assists in moving data from this StateManager's object to/from the
+    // store.  See provideFields(), replaceFields(), and the getXXXField()
+    // and providedXXXField() methods.
+    private FieldManager fieldManager;
+
+    // Reference to the instance requested to provide fields via fieldManager.
+    private Object expectedProvider;
+
+    // Class model information
+    private JDOClass jdoClass;
+
+    // Number of persistent fields
+    private int numFields = 0;
+
+    // JDOImplHelper instance
+    static final JDOImplHelper jdoImplHelper = 
+        (JDOImplHelper) AccessController.doPrivileged (
+        // Need to have privileges to perform JDOImplHelper.getInstance().
+            new PrivilegedAction () {
+                public Object run () {
+                    try {
+                        return JDOImplHelper.getInstance();
+                    }    
+                    catch (SecurityException e) {
+                        throw new JDOFatalUserException (msg.msg(
+                            "EXC_CannotGetJDOImplHelper"), e); // NOI18N
+                    }
+                }
+            }    
+        );
+
+    /** RuntimeJavaModelFactory. */
+    private static final RuntimeJavaModelFactory javaModelFactory =
+        (RuntimeJavaModelFactory) AccessController.doPrivileged(
+            new PrivilegedAction () {
+                public Object run () {
+                    return RuntimeJavaModelFactory.getInstance();
+                }
+            }
+        );
+
+    // ReachabilityHandler instance
+    private final static ReachabilityHandler reachabilityHandler = 
+        ReachabilityHandler.getInstance();
+
+    // SCOProcessor instance
+    private final static SCOProcessor scoProcessor = 
+        SCOProcessor.getInstance();
+
+    // Helper string
+    private final static String ChangeApplicationIdentityOption = 
+        "javax.jdo.option.ChangeApplicationIdentity"; // NOI18N
+
+    /**
+     * I18N message handler
+     */
+    private final static I18NHelper msg = 
+    	I18NHelper.getInstance("org.apache.jdo.impl.state.Bundle");  // NOI18N
+
+    /**
+     * Logger instance
+     */  
+    private static final Log logger = LogFactory.getFactory().getInstance(
+        "org.apache.jdo.impl.state"); // NOI18N
+
+    /** Register this class with the JDOImplHelper as an authorized class
+     * for replaceStateManager.
+     */
+    static {
+        AccessController.doPrivileged(
+            new PrivilegedAction () {
+                public Object run () {
+                    JDOImplHelper.registerAuthorizedStateManagerClass(StateManagerImpl.class);
+                    return null;
+                }
+            }
+        );
+    }
+    
+    /** Constructs a new <code>StateManagerImpl</code> to process
+     * future makePersistent request.
+     * @param pc the reference to the associated PersistenceCapable instance
+     * @param pm the reference to the associated
+     * PersistenceManagerInternal instance
+     */  
+    StateManagerImpl(PersistenceCapable pc, PersistenceManagerInternal pm) {
+        if (debugging())
+            debug("constructor with PC: " + pc); // NOI18N
+
+        myPC = pc;
+        myPCClass = pc.getClass();
+        initializePM(pm);
+        initializePC();
+    }
+
+    /** Constructs a new <code>StateManagerImpl</code> when requested
+     * from query processing.
+     * @param uoid the reference to the user object ID
+     * @param ioid the reference to the internal object ID
+     * @param pm the reference to the associated
+     * PersistenceManagerInternal instance
+     * @param clazz Class of the PersistenceCapable instance
+     */ 
+    StateManagerImpl(Object uoid, Object ioid, PersistenceManagerInternal pm,
+                     Class clazz) {
+        this.objectId = ioid; 
+        this.txObjectId = this.objectId;
+        if (debugging())
+            debug("constructor with user OID: " + uoid); // NOI18N
+
+        initializePM(pm);
+        StoreManager srm = myPM.getStoreManager();
+        myPCClass = clazz; 
+
+        if (uoid == null) { // Requested by the store.
+            initializePC();
+            myPC = jdoImplHelper.newInstance (myPCClass, this);
+            srm.copyKeyFieldsFromObjectId(this, myPCClass);
+            markPKFieldsAsLoaded();
+
+        } else if (srm.hasActualPCClass(ioid)){
+            initializePC();
+            myPC = jdoImplHelper.newInstance (myPCClass, this, uoid);
+            markPKFieldsAsLoaded();
+
+        } // else do nothing.
+
+        if (tx.isActive() && (tx.getOptimistic() == false)) { 
+            myLC = LifeCycleState.getLifeCycleState(LifeCycleState.HOLLOW);
+        } else {
+            myLC = LifeCycleState.getLifeCycleState(LifeCycleState.P_NON_TX);
+        }
+
+        if (debugging())
+            debug("constructor " + myLC + " for: " + myPCClass); // NOI18N
+
+        registerNonTransactional();
+    }
+
+    /** Initialize PC Class information.
+     */
+    private void initializePC() {
+        if (debugging())
+            debug("initializePC"); // NOI18N
+
+        jdoClass = javaModelFactory.getJavaType(myPCClass).getJDOClass();
+
+        // RESOLVE: remember JDOFields of FieldDescriptors.
+        numFields = jdoClass.getManagedFields().length;
+
+        loadedFields = new BitSet(numFields);
+
+        dirtyFields = new BitSet(numFields);
+        biFields = new BitSet(numFields);
+    }
+
+    /** Initialize PersistenceManager related information.
+     * @param pm the reference to the associated
+     * PersistenceManagerInternal instance
+     */
+    private void initializePM(PersistenceManagerInternal pm) {
+        if (debugging())
+            debug("initializePM: " + pm); // NOI18N
+
+        myPM = pm;
+        tx = myPM.currentTransaction();
+        PersistenceManagerFactory pmf = myPM.getPersistenceManagerFactory();
+        allowedChangeApplicationIdentity = pmf.supportedOptions().contains(
+            ChangeApplicationIdentityOption);
+
+    }
+
+    /** Mark PK fields as loaded:
+     */
+    private void markPKFieldsAsLoaded() {
+        int[] pkfields = jdoClass.getPrimaryKeyFieldNumbers();
+        for (int i = 0; i < pkfields.length; i++) {
+            if (debugging())
+                debug("markPKFieldsAsLoaded " + myPCClass.getName() + "." + // NOI18N
+                    getFieldName(pkfields[i])); 
+
+            loadedFields.set(pkfields[i]);
+        }
+    }
+
+    //
+    // ------------ State transition methods ------------
+    //
+
+    /** Transitions LifeCycleState on afterCompletion.
+     * @param abort true if rollback
+     * @param retainValues the flag that indicates how to proceed on commit.
+     * @param restoreValues the flag that indicates how to proceed on rollback.
+     */
+    public void afterCompletion(boolean abort, boolean retainValues, 
+            boolean restoreValues) {
+        inAfterCompletion = true;
+
+        if (debugging())
+            debug("afterCompletion " + myLC); // NOI18N
+
+        if (abort)
+            myLC = myLC.transitionRollback(restoreValues, this);
+        else
+            myLC = myLC.transitionCommit(retainValues, this);
+
+        if (debugging())
+            debug("afterCompletion " + myLC); // NOI18N
+
+        inAfterCompletion = false;
+    }
+
+    /** Transition to Persistent-New
+     */
+    public void makePersistent() {
+        LifeCycleState st = myLC;
+
+        if (debugging())
+            debug("makePersistent " + myLC); // NOI18N
+
+        if (myLC == null) {
+            // New request.
+            initializeSM(LifeCycleState.P_NEW);
+        } else {
+            // LifeCycle is already asigned.
+            myLC = myLC.transitionMakePersistent(this);
+        }
+        if (st != myLC) {
+            // It was not a no-op...
+            createAllBeforeImage();
+
+            // Replace java.util SCO instances with tracked SCOs.
+            replaceSCOFields();
+            processReachability(false);
+            setSCOOwner(true);
+        }
+        if (debugging())
+            debug("makePersistent " + myLC); // NOI18N
+    }
+
+    /** delete persistencecapable
+     */
+    public void deletePersistent() {
+        if (debugging())
+            debug("deletePersistent " + myLC); // NOI18N
+
+        myLC = myLC.transitionDeletePersistent(this);
+        //deleteRelationships();
+        if (debugging())
+            debug("deletePersistent " + myLC); // NOI18N
+    }
+
+    /** Transition to Transactional
+     */  
+    public void makeTransactional() {
+        if (debugging())
+            debug("makeTransactional " + myLC); // NOI18N
+
+        if (myLC == null) {
+            // New request.
+            initializeSM(LifeCycleState.T_CLEAN);
+            markAllDirty();
+            createBeforeImage();
+        } else if (tx.isActive()); {
+            // LifeCycle is already asigned.
+            myLC = myLC.transitionMakeTransactional(this, tx);
+        }
+        if (debugging())
+            debug("makeTransactional " + myLC); // NOI18N
+    }
+
+    /** Transition to Nontransactional
+     */  
+    public void makeNontransactional() {
+        if (debugging())
+            debug("makeNontransactional " + myLC); // NOI18N
+
+        myLC = myLC.transitionMakeNontransactional(this, tx);
+        if (debugging())
+            debug("makeNontransactional " + myLC); // NOI18N
+    }
+
+    /** Transition to Transient
+     */  
+    public void makeTransient() {
+        if (debugging())
+            debug("makeTransient " + myLC); // NOI18N
+
+        myLC = myLC.transitionMakeTransient(this, tx);
+        if (debugging())
+            debug("makeTransient " + myLC); // NOI18N
+    }
+
+    /** Transition to Hollow
+     */  
+    public void evictInstance() {
+        if (debugging())
+            debug("evictInstance " + myLC); // NOI18N
+
+        myLC = myLC.transitionEvict(this, tx);
+        if (debugging())
+            debug("evictInstance " + myLC); // NOI18N
+    }
+
+    /** Transition to Clean
+     */  
+    public void refreshInstance() {
+        if (debugging())
+            debug("refreshInstance " + myLC); // NOI18N
+
+        myLC = myLC.transitionRefresh(this, tx);
+        if (debugging())
+            debug("refreshInstance " + myLC); // NOI18N
+    }
+
+    /** Transition on retrieve request. This fetches Hollow instance
+     * and transitions to the appropriate LifeCycle state.
+     */  
+    public void retrieve() {
+        if (debugging())
+            debug("retrieve " + myLC); // NOI18N
+
+        myLC = myLC.transitionRetrieve(this, tx);
+        if (debugging())
+            debug("retrieve " + myLC); // NOI18N
+    }
+    
+    /**
+     * Transition the lifecycle state as if the instance is retrieved from the 
+     * datastore, but use the specified field values instead of loading them 
+     * from the datastore.
+     * @param fields Indicates which fields should be replaced in the PC.
+     * @param fieldManager FieldManager from which the field's value should be
+     * obtained.
+     */
+    // Synchronized to avoid conflicts w.r.t. fieldManager.
+    public synchronized void replace(int[] fields, FieldManager fieldManager) {
+        if (debugging())
+            debug("replace " + myLC); // NOI18N
+        
+        myLC = myLC.transitionReplace(this, tx, fields, fieldManager);
+         
+        if (debugging())
+            debug("replace " + myLC); // NOI18N
+    }
+     
+    /** Fetches or refreshes pc instance. Called by
+     * PersistenceManager.getObjectById with validate flag set to true.
+     */  
+    public void reload() {
+        if (debugging())
+            debug("reload " + myLC); // NOI18N
+
+        try {
+            myLC = myLC.transitionReload(this, tx);
+        } catch (JDOException e) {
+            // RESOLVE - disconnect or just deregister?
+            disconnect();
+            throw e;
+        }
+        if (debugging())
+            debug("reload " + myLC); // NOI18N
+    }
+
+    /**
+    * @see org.apache.jdo.state.StateManagerInternal#flush(StoreManager srm)
+    */
+    public boolean flush(StoreManager srm) {
+        LifeCycleState st = myLC;
+        try {
+            myLC = myLC.flush(loadedFields, dirtyFields, srm, this);
+        } catch (JDOException e) {
+            myLC = st;
+            throw e;
+        }
+        return true;
+    }
+
+    /** 
+     * @see org.apache.jdo.state.StateManagerInternal#handleReachability(
+     * boolean flag)
+     */
+    public void handleReachability(boolean commit) { 
+        if (myLC.isDeleted) {
+            // Don't do reachability for deleted instances.
+            return;
+        } else {
+            processReachability(commit);
+            if (!commit && myLC.isPersistent() && ! myLC.isAutoPersistent()) {
+                // Set owner only inside an active transaction and
+                // only for persistent instances.
+                setSCOOwner(true); 
+            }
+        }
+    }
+
+    /** 
+     * Replaces field values that are regular SCO instances with tracked SCOs.
+     * @see org.apache.jdo.state.StateManagerInternal#replaceSCOFields()
+     */
+    public void replaceSCOFields() {
+        SCO sco = null;
+        Object o = null;
+        Class cls = null;
+        JDOField jdoField = null;
+        int[] nonpkfields = jdoClass.getPersistentNonPrimaryKeyFieldNumbers();
+
+        for (int i = 0; i < nonpkfields.length; i++) {
+            int field = nonpkfields[i];
+            jdoField = jdoClass.getField(field);
+            if (isSCOType(javaModelFactory.getJavaClass(jdoField.getType()))) {
+                o = fetchObjectField(field);
+                sco = scoProcessor.getSCOField(o, jdoField, myPM);
+                replaceSCO(sco, field);
+
+            } // else primitive - do nothing.
+        }
+    }
+
+    /** 
+     * Unsets owner of tracked SCO field values and marks fields as not loaded.
+     */
+    protected void unsetSCOFields() {
+        Object o = null;
+        JDOField jdoField = null;
+        int[] nonpkfields = jdoClass.getPersistentNonPrimaryKeyFieldNumbers();
+        int tmp[] = new int[nonpkfields.length];
+        int l = 0;
+
+        for (int i = 0; i < nonpkfields.length; i++) {
+            int field = nonpkfields[i];
+            jdoField = jdoClass.getField(field);
+            if (isSCOType(javaModelFactory.getJavaClass(jdoField.getType()))) {
+                o = fetchObjectField(field);
+                if (org.apache.jdo.sco.SCO.class.isInstance(o)) {
+                    resetOwner((SCO)o, field, false);
+                    loadedFields.clear(field);
+                    tmp[l] = field;
+                    l++;
+                }
+
+            } // else primitive - do nothing.
+        }
+/* DO NOT SET TO NULL
+        // Now set those references to null.
+        int[] fields = new int[l];
+        System.arraycopy(tmp, 0, fields, 0, l);
+
+        this.fieldManager = hollowFieldManager; // Save for callback in giveXXXField
+
+        expectedProvider = myPC; // Save for verification.
+        myPC.jdoReplaceFields(fields);
+        expectedProvider = null; // No expected request.
+*/
+
+    }
+
+    /** 
+     * @see org.apache.jdo.state.StateManagerInternal#makeDirty (int field)
+     */  
+    public void makeDirty (int field) {
+        if (debugging())
+            debug("makeDirty " + field); // NOI18N
+
+        // Reload field if necessary:
+        if(myLC.isPersistent()) {
+            loadField(field);
+        }
+
+        if (debugging())
+            debug("makeDirty " + myLC); // NOI18N
+
+        myLC = myLC.transitionWriteField(this, tx);
+
+        if (debugging())
+            debug("makeDirty " + myLC); // NOI18N
+
+        updateBeforeImage(getFields(field));
+        dirtyFields.set(field);
+    }
+
+    /** 
+     * Makes newly added instances to an SCO Collection or SCO Map
+     * auto-persistent.
+     *
+     * @see org.apache.jdo.state.StateManagerInternal#trackUpdates (int field, 
+     * SCO sco)
+     */  
+    public void trackUpdates (int field, SCO sco) {
+        if (debugging())
+            debug("trackUpdates " + field); // NOI18N
+
+        scoProcessor.trackUpdates(this, field, sco);
+    }
+
+    /** 
+     * @see org.apache.jdo.state.StateManagerInternal#getFieldName (int field)
+     */  
+    public String getFieldName (int field) {
+        return jdoClass.getField(field).getName();
+    }
+
+    /** 
+     * @see org.apache.jdo.state.StateManagerInternal#getPCClass ()
+     */  
+    public Class getPCClass () {
+        return myPCClass;
+    }
+
+    /** 
+     * @see org.apache.jdo.state.StateManagerInternal#setPCClass (Class pcClass)
+     */  
+    public void setPCClass (Class pcClass) {
+        if (myPC == null) {
+            // myPC will be null if its class cannot be resolved 
+            // until fetch.
+            if (debugging())
+                debug("setPCClass " + myLC + " for: " + pcClass); // NOI18N
+
+            myPCClass = pcClass; 
+            initializePC();
+
+            myPC = jdoImplHelper.newInstance (myPCClass, this);
+            StoreManager srm = myPM.getStoreManager();
+            srm.copyKeyFieldsFromObjectId(this, myPCClass);
+            markPKFieldsAsLoaded();
+        }
+    }
+
+    /** Tests whether this StateManager represents a instance made persistent
+     * object.
+     *
+     * @see org.apache.jdo.state.StateManagerInternal#isNew ()
+     * @return <code>true</code> if this StateManager represents an
+     * instance made persistent in the current transaction.
+     */
+    public boolean isNew() {
+        return myLC.isNew();
+    }
+
+    //
+    // LifeCycleState transition requests
+    //
+
+    /** Transition to Auto-Persistent-New (persistence-by-reachability)
+     */
+    protected void makeAutoPersistent() {
+        LifeCycleState st = myLC;
+        if (debugging())
+            debug("makeAutoPersistent " + myLC); // NOI18N
+ 
+        if (myLC == null) {
+            initializeSM(LifeCycleState.AP_NEW);
+        } else {
+            myLC = myLC.transitionToAutoPersistent(this);
+        }      
+        if (st != myLC) {
+            // It was not a no-op...
+            createAllBeforeImage();
+            handleReachability(false); 
+        }
+        if (debugging())
+            debug("makeAutoPersistent " + myLC); // NOI18N
+    }
+
+    /** Processes Array of referenced objects for possible auto-persistence
+     * (persistence-by-reachability).
+     * @param o Array of referenced objects
+     */
+    protected void makeAutoPersistent(Object[] o) {
+        if (o != null) {
+            for (int i = 0; i < o.length; i++) {
+                if (o[i] != null) {
+                    reachabilityHandler.process(o[i], myPM, false);
+                } // else nothing to do.
+            }
+        }
+    }
+                
+    /** Set owner on elements of an Array of SCO objects.
+     * @param o array of referenced objects.
+     * @param field the field number.
+     * @param set true if owner field should be set, false if unset.
+     */  
+    protected void resetOwner(Object[] o, int field, boolean set) {
+        if (o == null) {
+            // Nothing to do.
+            return;
+        }
+        for (int i = 0; i < o.length; i++) {
+            if (o[i] != null) {
+                resetOwner(o[i], field, set);
+            } // else nothing to do.
+        }
+    }
+
+    /** Set owner on elements of an Iterator of SCO objects.
+     * @param it Iterator over referenced objects.
+     * @param field the field number.
+     * @param set true if owner field should be set, false if unset.
+     */  
+    protected void resetOwner(Iterator it, int field, boolean set) {
+        if (it == null) {
+            // Nothing to do.
+            return;
+        }
+        while (it.hasNext()) {
+            Object o = it.next();
+            if (o != null) {
+                if (o instanceof Map.Entry) {
+                    Map.Entry mapEntry = (Map.Entry)o;
+                    resetOwner(mapEntry.getKey(), field, set);
+                    resetOwner(mapEntry.getValue(), field, set);
+                } else {
+                    resetOwner(o, field, set);
+                }
+            }
+        }
+    }
+
+     /** Restore fields from beforeImage on commit or rollback
+      * called by LifeCycle on commit or rollback transition.
+      */
+    protected void restoreFields() {
+        //
+        // Only restore if there is a before image
+        //
+        if (beforeImage != null) {
+            myPC.jdoCopyFields(beforeImage, getFieldNums(loadedFields));
+        }                         
+    }
+
+    /** Clear fields on commit or rollback
+     * called by LifeCycle on commit or rollback transition.
+     */
+    protected void clearFields() {
+        if (javax.jdo.InstanceCallbacks.class.isInstance(myPC)) {
+            ((InstanceCallbacks)myPC).jdoPreClear();
+        }
+        // Unset owner on SCO fields.
+        setSCOOwner(false); 
+
+        // CLEAR FIELDS: myPC.clearFields();
+        this.fieldManager = hollowFieldManager; // Save for callback in giveXXXField
+
+        expectedProvider = myPC; // Save for verification.
+        myPC.jdoReplaceFields(jdoClass.getPersistentNonPrimaryKeyFieldNumbers());
+        expectedProvider = null; // No expected request.
+
+        loadedFields.andNot(loadedFields);
+        markPKFieldsAsLoaded();
+    }
+
+    /** Disconnect StateManager and PC. Called by LifeCycle when
+     * transition to Transient.
+     */
+    protected void disconnect() {
+        deregister();
+
+        if (myPC != null) {
+            // myPC will be null if its class cannot be resolved 
+            // until fetch.
+            // Need to set jdoFlag to READ_WRITE_OK for transient instance.
+            jdoFlags = PersistenceCapable.READ_WRITE_OK;
+            myPC.jdoReplaceFlags();
+
+            transitionTransient = true;
+            myPC.jdoReplaceStateManager(null);
+            transitionTransient = false;
+        }
+
+        resetRef();
+    }
+
+    /** Reset all settings
+     */
+    protected void reset() {
+        if (debugging())
+            debug("reset"); // NOI18N
+
+        beforeImage = null;
+        flushedImage = null;
+        this.txObjectId = this.objectId;
+
+         dirtyFields.andNot(dirtyFields);
+         biFields.andNot(biFields);
+    }
+
+    /** Refresh object inside of an active transaction as requested by
+     * the LifeCycle.
+     */  
+    protected void refresh() {
+        int[] fields = null;
+        if (myPC != null) {
+            // myPC will be null if its class cannot be resolved 
+            // until fetch.
+            resetDirtyFields();
+            fields = getFieldNums(loadedFields);
+            loadedFields.andNot(loadedFields);
+        }
+
+        fetch (myPM.getStoreManager(), fields);
+
+    }
+
+    /** Load all persistent fields as requested by the LifeCycle.
+     */  
+    protected void loadUnloaded() {
+        // Check if some fields were not loaded yet:
+        int[] fields = getUnloaded(jdoClass.getPersistentFieldNumbers(),
+            loadedFields);
+        if (fields != null && fields.length > 0) {
+            StoreManager srm = myPM.getStoreManager();
+            fetch (myPM.getStoreManager(), fields);
+        }
+    }   
+
+    /**
+    * Adds this StateManager to all caches
+    */
+    protected void registerTransactional() {
+        myPM.register(this, objectId, true, false);
+    }
+
+    /**
+    * Adds this StateManager to non-transactional caches
+    */
+    protected void registerNonTransactional() {
+        myPM.register(this, objectId, false, false);
+    }
+
+    /**
+    * Removes this StateManager from all the caches
+    */
+    protected void deregister() {
+        if (myLC.isPersistent()) {
+            myPM.deregister(objectId);
+        } else {
+            myPM.deregisterTransient(this);
+        }
+    }
+
+    // For some unknown reason, the @see for jdoPreDelete results in an error
+    // message from javadoc.  We don't know why.  We have tried to fix it,
+    // and have tried javadoc in JDK 1.3 and 1.4beta1, to no avail.
+    /** 
+     * @see javax.jdo.InstanceCallbacks#jdoPreDelete()
+     */
+    protected void preDelete() {
+        if (javax.jdo.InstanceCallbacks.class.isInstance(myPC)) {
+            ((InstanceCallbacks)myPC).jdoPreDelete();
+        }
+    }
+
+    /** If this class implements InstanceCallbacks, call the jdoPostLoad
+     * method. This is done after the default fetch group values have been 
+     * loaded from the store.
+     */
+    protected void postLoad() {
+        if (javax.jdo.InstanceCallbacks.class.isInstance(myPC)) {
+            ((InstanceCallbacks)myPC).jdoPostLoad();
+        }
+    }
+    
+    /**
+     * Called by LifeCycleState when transition persistent instance to the
+     * corresponding flushed state.
+     */
+    protected void markAsFlushed() {
+        //
+        // reset the current state to the point where we can accept more changes.
+        //
+        resetDirtyFields();
+        myPM.markAsFlushed(this);
+    }
+
+    /**
+     * Reset beforeImage on refresh or flush
+     */
+    protected void unsetBeforeImage() {
+        if (debugging())
+            debug("unsetBeforeImage"); // NOI18N
+
+        beforeImage = null;
+        biFields.andNot(biFields);
+    }
+
+    /**
+     * Create a new beforeImage in an active optimistic transaction or
+     * an active datastore transaction with restoreValues flag set to true
+     * or for a transient-transactional instance.
+     */
+    protected void createBeforeImage() {
+        if (debugging())
+            debug("createBeforeImage: new " + (beforeImage == null) + // NOI18N
+                " Tx is active: " + tx.isActive()); // NOI18N
+
+        boolean isTransientTransactional = 
+            !myLC.isPersistent() && myLC.isTransactional();
+        if (beforeImage == null && tx.isActive() && 
+            (tx.getOptimistic() || tx.getRestoreValues() || 
+             isTransientTransactional)) {
+
+            beforeImage = jdoImplHelper.newInstance (myPCClass, this);
+            int[] fields = getFieldNums(loadedFields);
+            beforeImage.jdoCopyFields(myPC, fields);
+            replaceSCOWithClones(fields);
+
+            biFields.or(loadedFields);
+        }
+    }
+
+    /**
+     * Verifies that this class type is a supported SCO type.
+     * @param type Class type to check.
+     * @return true if this type is  a supported SCO type.
+     */
+    protected boolean isSCOType(Class type) {
+        return myPM.isSupportedSCOType(type);
+    }
+
+    //
+    // Private helper methods for state transitions
+    //
+
+    /**
+     * Replaces SCO instances with clones in the before image
+     * to preserve the state.
+     * @param fields array of field numbers to process.
+     */
+    private void replaceSCOWithClones(int[] fields) {
+        for (int i = 0; i < fields.length; i++) {
+            int field = fields[i];
+            JDOField jdoField = jdoClass.getField(field);
+            Class type = javaModelFactory.getJavaClass(jdoField.getType());
+            if (isSCOType(type)) {
+                Object o = fetchObjectField(field);
+                if (o == null) {
+                    // Nothing to do. 
+                    continue;
+                }
+                Object clone = null;
+                if (o instanceof SCO) {
+                    clone = ((SCO)o).clone();
+                } else {
+                    try {
+                        java.lang.reflect.Method m = 
+                            o.getClass().getMethod("clone", null); // NOI18N
+                        if (m != null) {
+                            clone = m.invoke(o, null);
+                        }
+                    } catch (Exception e) {
+                        throw new JDOFatalInternalException(
+                            msg.msg("EXC_SCONotCloneable", o.getClass().getName())); //NOI18N
+                    }
+                }
+                fieldManager = objectFieldManager;
+                fieldManager.storeObjectField(field, clone);
+                expectedProvider = beforeImage; // Save for verification.
+                beforeImage.jdoReplaceField(field) ; //getFields(field));
+                expectedProvider = null; // No expected request.
+            }
+        }
+    }
+
+    /** Returns current value from the Object type field.
+     * @param field the field number
+     * @return current value as Object.
+     */  
+    private Object fetchObjectField(int field) {
+        this.fieldManager = objectFieldManager;
+
+        expectedProvider = myPC; // Save for verification.
+        myPC.jdoProvideField(field);
+        expectedProvider = null; // No expected request.
+
+        return fieldManager.fetchObjectField(field);
+    }
+
+    /** Transition referenced fields to Persistent at commit
+     * (persistence-by-reachability)
+     * @param commit true if it is called during commit.
+     */  
+    private void processReachability(boolean commit) {
+       int[] relfields = jdoClass.getPersistentRelationshipFieldNumbers();
+       for (int i = 0; i < relfields.length; i++) {
+            Object o = fetchObjectField(relfields[i]);
+            if (o != null) {
+                reachabilityHandler.process(o, myPM, commit);
+            } 
+        }
+    }
+
+    /** Set owner on referenced SCO objects.
+     * @param o referenced object.
+     * @param field the field number.
+     * @param set true if owner field should be set, false if unset.
+     */  
+    private void resetOwner(Object o, int field, boolean set) {
+        if (o == null) {
+            return;
+
+        } else if(org.apache.jdo.sco.SCO.class.isInstance(o)) {
+            resetOwner((SCO)o, field, set);
+
+        } else { // check for elements of Arrays.
+            Class c = o.getClass();
+            if (c.isArray() && !c.getComponentType().isPrimitive()) {
+                resetOwner((Object[])o, field, set);
+            } // else do nothing
+        }
+    }
+
+    /** Set owner on referenced SCO objects.
+     * @param sco referenced SCO object.
+     * @param field the field number.
+     * @param set true if owner field should be set, false if unset.
+     */  
+    private void resetOwner(SCO sco, int field, boolean set) {
+        if (set) {
+            sco.setOwner(this, field);
+        } else {
+            sco.unsetOwner(this, field);
+        }
+
+        // Verify elements of an SCO Collection:
+        if (org.apache.jdo.sco.SCOCollection.class.isInstance(sco)) {
+            SCOCollection scoCollection = (SCOCollection)sco;
+            if (set) {
+                resetOwner(scoCollection.iterator(), field, set);
+            } else {
+                // if collection is frozen, don't thaw it to unset owner.
+                resetOwner(scoCollection.eitherIterator(), field, set);
+            }
+
+        // Verify elements and keys of an SCO Map:
+        } else if (org.apache.jdo.sco.SCOMap.class.isInstance(sco)) {
+            SCOMap scoMap = (SCOMap)sco;
+            if (set) {
+                resetOwner(scoMap.entrySet().iterator(), field, set);
+            } else {
+                // if map is frozen, don't thaw it to unset owner.
+                resetOwner(scoMap.eitherIterator(), field, set);
+            }
+
+        } // else do nothing
+    }
+
+    /** Load field value if necessary
+     * @param field the field number
+     */  
+    private void loadField (int field) {
+        myPM.assertReadAllowed();
+        LifeCycleState st = myLC;
+        if (debugging())
+            debug("loadField " + myPCClass.getName() + "."  // NOI18N
+                + getFieldName(field) + " " + myLC); // NOI18N
+
+        try {
+            if (!loadedFields.get(field)) {
+                StoreManager srm = myPM.getStoreManager();
+                fetch (srm, getFields(field));
+            }
+            myLC = myLC.transitionReadField(this, tx);
+
+        } catch (JDOException e) {
+            if (!st.isTransactional() && myLC.isTransactional()) {
+                registerNonTransactional();
+            }
+            myLC = st;
+            throw e;
+        }
+        if (debugging())
+            debug("Done: loadField " + myPCClass.getName() + "." +  // NOI18N
+                getFieldName(field) + " " + myLC); // NOI18N
+    }
+
+    /** Preparation steps for replacingXXXField operation
+     * @param pc the calling PersistenceCapable instance
+     * @param field the field number
+     */  
+    private void loadingField(Object pc, int field) {
+        if (myPC == pc && !inAfterCompletion) {
+            if (debugging())
+                debug("loadingField " + myPCClass.getName() + "." + // NOI18N
+                    getFieldName(field)); 
+
+            loadedFields.set(field);
+        }
+    }
+
+    /** Preparation steps for setXXXField operation for non-Object type field.
+     * @param pc the calling PersistenceCapable instance
+     * @param field the field number
+     * @param fieldManager the FieldManager that handles double-dispatch
+     */  
+    private void prepareSetField(PersistenceCapable pc, int field, 
+        FieldManager fieldManager) {
+        // Check if it is attempt to change PrimaryKey value:
+        boolean isPrimaryKey =  jdoClass.getField(field).isPrimaryKey();
+        if (!allowedChangeApplicationIdentity && isPrimaryKey) {
+            throw new JDOUnsupportedOptionException(
+                ChangeApplicationIdentityOption);
+        }
+        prepareSetField1(pc, field);
+        prepareSetField2(field, fieldManager);
+    }
+
+    /** Preparation steps for setXXXField operation for Object type field.
+     * @param pc the calling PersistenceCapable instance
+     * @param field the field number
+     * @param fieldManager the FieldManager that handles double-dispatch
+     * @param currentValue current value of the field.
+     * @param newValue the new value of the field.
+     */  
+    private void prepareSetField(PersistenceCapable pc, int field, 
+        FieldManager fieldManager, Object currentValue, Object newValue) {
+
+        boolean present = loadedFields.get(field);
+        prepareSetField1(pc, field);
+
+        // now the value is populated, if it was not:
+        if (!present) {
+            currentValue = fetchObjectField(field);
+        }
+
+        boolean replace = (newValue != currentValue);
+
+        // Verify elementType of a new SCO before proceeding further:
+        if (replace) {
+            assertSCOElementType(newValue, field);
+        }
+
+        prepareSetField2(field, fieldManager);
+
+        // Everything is fine, adjust the ownership on SCO objects:
+        if (replace) {
+            if (currentValue != null &&
+                org.apache.jdo.sco.SCO.class.isInstance(currentValue)) {
+                // Unset owner on SCO instance that is not referenced any more.
+                resetOwner(currentValue, field, false);
+            }
+            if (newValue != null &&
+                org.apache.jdo.sco.SCO.class.isInstance(newValue)) {
+                // This is the first time we set it:
+                resetOwner(newValue, field, true);
+            }
+        }
+    }
+
+     /** Verification and load part of the preparation steps for
+     * setXXXField operation.
+     * @param pc the calling PersistenceCapable instance
+     * @param field the field number
+     */  
+    private void prepareSetField1(PersistenceCapable pc, int field) {
+        if (myPC != pc) {
+            // It is clone that we will disconnect. But before set the
+            // field value:
+            expectedProvider = pc; // Save for verification.
+            pc.jdoReplaceField(field) ; //getFields(field));
+            expectedProvider = null; // No expected request.
+
+            disconnectClone(pc);
+        }
+
+        // Reload field if necessary:
+        if(myLC.isPersistent()) {
+            loadField(field);
+        }
+    }
+
+    /** Transtion write access and replace value step for setXXXField operation.
+     * @param field the field number
+     * @param fieldManager the FieldManager that handles double-dispatch
+     */   
+    private void prepareSetField2(int field, FieldManager fieldManager) {
+        LifeCycleState st = myLC;
+
+        if (debugging())
+            debug("prepareSetField2 " + myLC); // NOI18N
+
+        myLC = myLC.transitionWriteField(this, tx);
+
+        if (debugging())
+            debug("prepareSetField2 " + myLC); // NOI18N
+
+        this.fieldManager = fieldManager; // Save for callback in giveXXXField
+        expectedProvider = myPC; // Save for verification.
+        myPC.jdoReplaceField(field) ; //getFields(field));
+        expectedProvider = null; // No expected request.
+
+        dirtyFields.set(field);
+    }
+    
+    /**
+     * Update existing beforeImage in a transaction.
+     */
+    private void updateBeforeImage(int[] fields) {
+        //Do NOT create beforeImage here - this is update only.
+        if (beforeImage != null) { 
+            fields = getUnloaded(fields, biFields);
+            if (fields != null && fields.length > 0) {
+                if (debugging()) {
+                    debug("updateBeforeImage ["); // NOI18N
+
+                    for (int i = 0; i < fields.length; i++) 
+                        logger.debug (fields[i] + " "); // NOI18N
+                    logger.debug("]"); // NOI18N
+                }
+                beforeImage.jdoCopyFields(myPC, fields);
+                replaceSCOWithClones(fields);
+            }
+            biFields.or(loadedFields);
+        }
+    }
+
+    /**
+     * Replace field value with tracked SCO.
+     * @param sco tracked SCO instance to be replaced.
+     * @param field the field number.
+     */
+    private void replaceSCO(SCO sco, int field) {
+        this.fieldManager = objectFieldManager;
+
+        if (sco != null) {
+            resetOwner(sco, field, true);
+
+            // Replace the field
+            fieldManager.storeObjectField(field, sco);
+            expectedProvider = myPC; // Save for verification.
+            myPC.jdoReplaceField(field) ; //getFields(field));
+            expectedProvider = null; // No expected request.
+        }
+    }
+
+    /** 
+     * Change owner of all SCO fields. 
+     *
+     * @param set true if owner should be set, false if references 
+     * to this SCO instance will be nullified and owner to be set to null.
+     */
+    private void setSCOOwner(boolean set) {
+        Object o = null;
+        Class cls = null;
+        JDOField jdoField = null;
+        int[] nonpkfields = jdoClass.getPersistentNonPrimaryKeyFieldNumbers();
+
+        Throwable[] err = new Throwable[nonpkfields.length];
+        int l = 0;
+        for (int i = 0; i < nonpkfields.length; i++) {
+            int field = nonpkfields[i];
+
+            jdoField = jdoClass.getField(field);
+            if (isSCOType(javaModelFactory.getJavaClass(jdoField.getType()))) {
+                o = fetchObjectField(field);
+                try {
+                    if (set) {
+                        scoProcessor.assertSCOElementType(o, jdoField);
+                    }
+                    resetOwner(o, field, set);
+                } catch (Throwable e) {
+                    err[l++] = e;
+                }
+            } // else not an Object field - do nothing
+        }
+
+        if (l > 0) {
+            Throwable[] t = new Throwable[l];
+            System.arraycopy(err, 0, t, 0, l);
+            throw new JDOUserException(msg.msg(
+                "EXC_FailedToProcessAll"), t); // NOI18N
+        }
+    }
+
+    /** Assert element type of an SCO Collection or key and value types
+     * of an SCO Map.
+     * @param o Object to be tested.
+     * @param field the corresponding field number.
+     * @throws JDOUserException if assertion fails.
+     */  
+    private void assertSCOElementType(Object o, int field) {
+        scoProcessor.assertSCOElementType(o, jdoClass.getField(field));
+    }
+
+    /** Returns external representation of the transactional object id
+     * that can be used by the client
+     */
+    private Object getTransactionalObjectId () {
+        StoreManager srm = myPM.getStoreManager();
+        Object oid = null;
+
+        if (flushedImage != null) {
+            oid = srm.getExternalObjectId(txObjectId, flushedImage);
+        } else if (beforeImage != null) {
+            oid = srm.getExternalObjectId(txObjectId, beforeImage);
+        } else {
+            oid = srm.getExternalObjectId(txObjectId, myPC);
+        }
+        return oid;
+    }
+
+    /** Create beforeImage for all fields - called by transition from TRANSIENT
+     * to P_NEW 
+     */
+    private void createAllBeforeImage() {
+        markAllDirty();
+        if (debugging())
+            debug("createBeforeImage"); // NOI18N
+
+        boolean isTransientTransactional = 
+            !myLC.isPersistent() && myLC.isTransactional();
+        if (tx.getOptimistic() || tx.getRestoreValues() ||
+            isTransientTransactional) {
+
+            beforeImage = jdoImplHelper.newInstance (myPCClass, this);
+            beforeImage.jdoCopyFields(myPC, getFieldNums(loadedFields));
+            biFields.or(loadedFields);
+        }
+    } 
+
+    /** Mark all fields as loaded and dirty - called by transition from 
+     * TRANSIENT to P_NEW and T_CLEAN
+     */
+    private void markAllDirty() {
+        for (int i = 0; i < numFields; i++) {
+            loadedFields.set(i);
+            dirtyFields.set(i); 
+        }
+    } 
+
+    /** Initialize SM reference in PC and Oid
+     */
+    private void initializeSM(int newState) {
+        myLC = LifeCycleState.getLifeCycleState(newState);
+        final StateManagerImpl thisSM = this;
+
+        try {
+            if (myLC.isPersistent()) {
+                StoreManager srm = myPM.getStoreManager();
+                objectId = srm.createObjectId(this, myPM);
+
+                myPM.register(this, objectId, true, true);
+                this.txObjectId = this.objectId;
+
+            } else {
+                myPM.registerTransient(this);
+            }
+
+            // Everything OK so far. Now we can set SM reference in PC 
+            // It can be done only after myLC is set to deligate validation
+            // to the LC and objectId verified for uniqueness
+        AccessController.doPrivileged (
+            // Need to have privileges to perform jdoReplaceStateManager.
+                new PrivilegedAction () {
+                    public Object run () {
+                        try {
+                            myPC.jdoReplaceStateManager(thisSM);
+                            return null;
+                        }    
+                        catch (SecurityException e) {
+                            throw new JDOFatalUserException (msg.msg(
+                                "EXC_CannotSetStateManager"), e); // NOI18N
+                        }
+                    }
+                }
+            );
+
+        } catch (SecurityException e) {
+            throw new JDOUserException(e.getMessage());
+
+        } catch (JDOException e1) {
+            if (myPM.getStateManager(objectId, myPCClass) == this) {
+                deregister();
+            }
+            resetRef();
+            throw e1;
+        }
+    }
+
+     /** Clear dirtyFields list on flush
+      */ 
+    private void resetDirtyFields() {
+        // RESOLVE: flushed Oid
+        dirtyFields.andNot(dirtyFields);
+    }
+
+    /** Reset all references to null
+     */
+    private void resetRef() {
+        if (debugging())
+            debug("resetRef"); // NOI18N
+
+        myPC = null;
+        myPM = null;
+        myLC = null;
+        jdoClass = null;
+        myPCClass = null;
+        beforeImage = null;
+        flushedImage = null;
+        this.txObjectId = null;
+        this.objectId = null;
+    }
+
+    /** Desconnects clone instance.
+     * @return true if it was clone.
+     */
+    private boolean disconnectClone(PersistenceCapable pc) {
+        if (pc != myPC) {
+            // Replace jdoFlags. It will be set to
+            // PersistenceCapable.READ_WRITE_OK
+            // by replacingFlags().
+            pc.jdoReplaceFlags();
+
+            // Reset StateManager:
+            pc.jdoReplaceStateManager(null);
+
+            return true;
+        }
+        return false;
+    }
+
+    /** Verifies field provider
+     * @throws JDOUserException if provider is not the one expected.
+     */
+    private boolean verifyProvider(PersistenceCapable pc) {
+        if (pc != expectedProvider) {
+            if (pc == myPC || pc == beforeImage || pc == flushedImage) {
+                throw new JDOFatalInternalException(
+                    msg.msg("EXC_WrongProvider")); // NOI18N
+            }
+            disconnectClone(pc);
+            return false;
+        }
+        return true;
+    }
+
+    /** Fetches instance from the data store
+     */  
+    private void fetch(StoreManager srm) {
+        fetch(srm, null);
+    }
+
+    /** Fetches specific fields in the instance from the data store 
+     */  
+    private void fetch(StoreManager srm, int[] fetchFields) {
+        srm.fetch(this, fetchFields);
+    }
+
+    /**
+    * Helper method to define the list of fields to be loaded
+    * together with this field
+    */
+    private int[] getFields(int field) {
+        // RESOLVE: SRM.dosomething(field)
+        fieldArr[0] = field;
+        return fieldArr;
+    }
+
+    /**
+    * Helper method to convert not loaded bits to field numbers.
+    */
+    private int[] getUnloaded(int[] newfields, BitSet set) {
+        int l = 0;
+        int tmp[] = new int[newfields.length];
+
+        for (int i = 0; i < newfields.length; i++) {
+            if (set.get(newfields[i]) == false) {
+                tmp[l] = newfields[i];
+                l++;
+            }
+        }
+
+        int[] fields = new int[l];
+        System.arraycopy(tmp, 0, fields, 0, l);
+
+        return fields;
+    }
+
+    /**
+    * Helper method to convert set bits in a BitSet to field numbers.
+    */
+    private int[] getFieldNums(BitSet bs) {
+        int length = 0;
+        int tmp[] = new int[numFields];
+
+        for (int i = 0; i < numFields; i++) {
+            if (bs.get(i) == true) {
+                tmp[length] = i;
+                length++;
+            }
+        }
+
+        int[] fields = new int[length];
+        System.arraycopy(tmp, 0, fields, 0, length);
+
+        return fields;
+    }
+
+    //
+    // ------------ End methods for state transitions ------------
+    //
+
+    /** The owning StateManager uses this method to supply the 
+     * value of the flags to the PersistenceCapable instance.
+     * @param pc the calling PersistenceCapable instance
+     * @return the value of jdoFlags to be stored in the
+     * PersistenceCapable instance
+     */
+    public byte replacingFlags(PersistenceCapable pc) {
+        if (myPC != pc) {
+            // This is clone - set flags as in a transient instance:
+            return PersistenceCapable.READ_WRITE_OK;
+        }
+        return jdoFlags;
+    }
+
+    /** Replace the current value of jdoStateManager.
+     * This method is called by the PersistenceCapable whenever 
+     * jdoReplaceStateManager is called and there is already
+     * an owning StateManager.  This is a security precaution
+     * to ensure that the owning StateManager is the only
+     * source of any change to itself.
+     * @param pc the calling PersistenceCapable instance
+     * @return the new value for the jdoStateManager
+     */ 
+    public StateManager replacingStateManager (PersistenceCapable pc,
+                                               StateManager sm) {
+        if (myLC == null) {
+            // This should never happen. LifeCycle is set the first
+            // thing on makePersistent and makeTransactional.
+            throw new JDOFatalInternalException(msg.msg(
+               "EXC_NullLifeCycle")); // NOI18N
+        }
+
+        // This is the same PC - not beforeImage or clone.
+        if (myPC == pc) {
+
+            // This call back should happen only when LifeCycle is
+            // transitioning to Transient
+            if (sm == null) { // transitioning to transient...
+                if (!transitionTransient) { // we are NOT transitioning to transient
+                    throw new JDOFatalInternalException(msg.msg( 
+                        "EXC_NotTransitionTransient")); // NOI18N
+                }
+                return null; // OK.
+
+            } else if (sm == this) { // should not happen
+                    throw new JDOFatalInternalException(msg.msg( 
+                        "EXC_SameStateManager")); // NOI18N
+
+            } else if (this.myPM == ((StateManagerImpl)sm).myPM) {
+                // same PM && sm != null && sm != this
+                // This is a race condition when makePersistent or
+                // makeTransactional is called on the same PC instance
+                // for the same PM. It has been already set to this SM
+                // - just disconnect the other one. Return this SM so
+                // it won't be replaced. 
+                ((StateManagerImpl)sm).disconnect();
+                return this;
+
+            } else { // another PM && sm != null && sm != this
+                // This is race condition when makePersistent or
+                // makeTransactional is called on the same PC instance
+                // for different PM
+                throw new JDOUserException(msg.msg(
+                    "EXC_PersistentInAnotherPersistenceManager"));// NOI18N
+            }
+
+        } else if (pc != beforeImage) {
+            /* This indicates that the clone is calling the method.
+             * Either the clone is trying to become persistent or we told it
+             * to become transient.
+             */
+            if (sm != null) {
+                // It is not becoming transient - force it:
+                ((StateManagerImpl)sm).disconnect();
+                disconnectClone(pc);
+            }
+            return null;
+        }
+        return null;
+    }
+    
+    /** Return the PersistenceManager that owns this instance.
+     * Called from internal methods. No validation performed.
+     * @return the PersistenceManager that owns this instance
+     */    
+     public PersistenceManagerInternal getPersistenceManager () {
+        if (debugging())
+            debug("getPersistenceManager " + myPM); // NOI18N
+
+        return myPM;
+     }
+
+    /** Return the PersistenceManager that owns this instance as
+     * PersistenceManager wrapper. If called by PersistenceManagerImpl, it
+     * will perform validation during this call.
+     * @param pc the calling PersistenceCapable instance
+     * @return the PersistenceManager that owns this instance
+     */    
+    public PersistenceManager getPersistenceManager (PersistenceCapable pc) {
+        if (disconnectClone(pc)) {
+            // This was clone. It should be transient now:
+            return null;
+        } else {
+            myPM.hereIsStateManager(this, myPC);
+            return myPM.getCurrentWrapper();
+        }
+    }
+
+    /** Mark the associated PersistenceCapable field dirty.
+     * <P> The StateManager will make a copy of the field
+     * so it can be restored if needed later, and then mark
+     * the field as modified in the current transaction.
+     * @param pc the calling PersistenceCapable instance
+     * @param fieldName the name of the field
+     */    
+    public void makeDirty (PersistenceCapable pc, String fieldName) {
+        if (debugging())
+            debug("makeDirty " + fieldName); // NOI18N
+
+        if (disconnectClone(pc)) {
+            // This was clone. It should be transient now:
+            return;
+        }
+        JDOField f = jdoClass.getManagedField(fieldName);
+        if (f != null) {
+            this.makeDirty(f.getFieldNumber());
+        } // else non-managed field - do nothing.
+    }
+
+    /** Return the object representing the JDO identity 
+     * of the calling instance.  If the JDO identity is being changed in
+     * the current transaction, this method returns the identity as of
+     * the beginning of the transaction.
+     * @param pc the calling PersistenceCapable instance
+     * @return the object representing the JDO identity of the calling instance
+     */    
+    public Object getObjectId (PersistenceCapable pc) {
+        if(disconnectClone(pc)) {
+            // This was clone. It should be transient now:
+            return null;
+        }
+        return getExternalObjectId();
+    }
+    
+    /** Return the object representing the JDO identity 
+     * of the calling instance.  If the JDO identity is being changed in
+     * the current transaction, this method returns the current identity as
+     * changed in the transaction.
+     * @param pc the calling PersistenceCapable instance
+     * @return the object representing the JDO identity of the calling instance
+     */    
+    public Object getTransactionalObjectId (PersistenceCapable pc){
+        if (disconnectClone(pc)) {
+            // This was clone. It should be transient now:
+            return null;
+        }
+        return getTransactionalObjectId();
+    }
+
+
+    /** Tests whether this object is dirty.
+     *   
+     * Instances that have been modified, deleted, or newly
+     * made persistent in the current transaction return true.
+     *   
+     *<P>Transient instances return false.
+     *<P>
+     * @see javax.jdo.spi.PersistenceCapable#jdoMakeDirty(String fieldName)
+     * @param pc the calling PersistenceCapable instance
+     * @return true if this instance has been modified in the current
+     * transaction.
+     */  
+    public boolean isDirty(PersistenceCapable pc) {
+        if (disconnectClone(pc) || myLC == null)
+            return false;
+
+        return myLC.isDirty();
+    }
+
+    /** Tests whether this object is transactional.
+     *   
+     * Instances that respect transaction boundaries return true.
+     * These instances include transient instances made transactional
+     * as a result of being the target of a makeTransactional method
+     * call; newly made persistent or deleted persistent instances;
+     * persistent instances read in data store transactions; and
+     * persistent instances modified in optimistic transactions.
+     *   
+     *<P>Transient instances return false.
+     *<P>
+     * @param pc the calling PersistenceCapable instance
+     * @return true if this instance is transactional.
+     */  
+    public boolean isTransactional(PersistenceCapable pc) { 
+        if (disconnectClone(pc) || myLC == null) 
+            return false;
+ 
+        return myLC.isTransactional(); 
+    } 
+
+
+    /** Tests whether this object is persistent.
+      *   
+      * Instances whose state is stored in the data store return true.
+      *   
+      *<P>Transient instances return false.
+      *<P>
+      * @see PersistenceManager#makePersistent(Object pc)
+      * @param pc the calling PersistenceCapable instance
+      * @return true if this instance is persistent.
+      */  
+    public boolean isPersistent(PersistenceCapable pc) {  
+        if (disconnectClone(pc) || myLC == null)  
+            return false; 
+  
+        return myLC.isPersistent();  
+    }  
+
+    /** Tests whether this object has been newly made persistent.
+      *
+      * Instances that have been made persistent in the current transaction
+      * return true.
+      *
+      *<P>Transient instances return false.
+      *<P>
+      * @see PersistenceManager#makePersistent(Object pc)
+      * @param pc the calling PersistenceCapable instance
+      * @return true if this instance was made persistent
+      * in the current transaction.
+      */
+    public boolean isNew(PersistenceCapable pc) {
+        if (disconnectClone(pc) || myLC == null)
+            return false;
+
+        return myLC.isNew();
+    }
+
+    /** Tests whether this object has been deleted.
+      *
+      * Instances that have been deleted in the current transaction return true.
+      *
+      *<P>Transient instances return false.
+      *<P>
+      * @see PersistenceManager#deletePersistent(Object pc)
+      * @param pc the calling PersistenceCapable instance
+      * @return true if this instance was deleted
+      * in the current transaction.
+      */
+    public boolean isDeleted(PersistenceCapable pc) { 
+        if (disconnectClone(pc) || myLC == null) 
+            return false; 
+ 
+        return myLC.isDeleted(); 
+    } 
+
+    /** Guarantee that the serializable transactional and persistent fields
+     * are loaded into the instance.  This method is called by the generated
+     * or user-written writeObject method prior to serialization of the
+     * instance.
+     * @param pc the calling PersistenceCapable instance
+     */  
+    public void preSerialize (PersistenceCapable pc) {
+        if (disconnectClone(pc)) {
+            // It was clone that we disconnected. Return as
+            // for any transient instance.
+            return ;
+        }
+
+        if (debugging())
+            debug("preSerialize " + myLC); // NOI18N
+
+        myLC = myLC.transitionReload(this, tx);
+
+        if (debugging())
+            debug("preSerialize " + myLC); // NOI18N
+
+        // Check if some fields were not loaded yet:
+        int[] fields = getUnloaded(
+            jdoClass.getPersistentSerializableFieldNumbers(), loadedFields);
+
+        // RESOLVE: how to skip java transient fields:
+        if (fields != null && fields.length > 0) {
+            StoreManager srm = myPM.getStoreManager();
+            fetch (myPM.getStoreManager(), fields);
+        }
+    }
+
+    /** This implementation of isLoaded will always return true.
+     * So the getXXXField methods do not ever need to be implemented.
+     * @param pc the calling PersistenceCapable instance
+     * @param field the field number
+     * @return true
+     */    
+    public boolean isLoaded (PersistenceCapable pc, int field) {
+        if (disconnectClone(pc)) {
+            // It was clone that we disconnected. Return true as
+            // for any transient instance.
+            return true;
+        }
+
+        loadField(field);
+
+        return loadedFields.get(field);
+    }
+
+    //
+    // getXXXField methods
+    //
+    // In the RI, these methods all throw a JDOFatalInternalException, since
+    // their implementation is not needed.  See StateManagerImpl.isLoaded
+    //
+
+    /**
+    * @see javax.jdo.spi.StateManager#getBooleanField(
+    * PersistenceCapable pc, int field, boolean currentValue)
+    */
+    public boolean getBooleanField(PersistenceCapable pc, int field,
+                                   boolean currentValue) {
+        notNeededByRI("getBooleanField"); // NOI18N
+        return false;
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getCharField(PersistenceCapable
+    * pc, int field, char currentValue)
+    */
+    public char getCharField(PersistenceCapable pc, int field,
+                             char currentValue) {
+        notNeededByRI("getCharField"); // NOI18N
+        return 'a';
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getByteField(
+    * PersistenceCapable pc, int field, byte currentValue)
+    */
+    public byte getByteField(PersistenceCapable pc, int field,
+                             byte currentValue) {
+        notNeededByRI("getByteField"); // NOI18N
+        return 0;
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getShortField(
+    * PersistenceCapable pc, int field, short currentValue)
+    */
+    public short getShortField(PersistenceCapable pc, int field,
+                               short currentValue) {
+        notNeededByRI("getShortField"); // NOI18N
+        return 0;
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getIntField(
+    * PersistenceCapable pc, int field, int currentValue)
+    */
+    public int getIntField(PersistenceCapable pc, int field,
+                           int currentValue) {
+        notNeededByRI("getIntField"); // NOI18N
+        return 0;
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getLongField(
+    * PersistenceCapable pc, int field, long currentValue)
+    */
+    public long getLongField(PersistenceCapable pc, int field,
+                             long currentValue) {
+        notNeededByRI("getLongField"); // NOI18N
+        return 0;
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getFloatField(
+    * PersistenceCapable pc, int field, float currentValue)
+    */
+    public float getFloatField(PersistenceCapable pc, int field,
+                               float currentValue) {
+        notNeededByRI("getFloatField"); // NOI18N
+        return 0.0F;
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getDoubleField(
+    * PersistenceCapable pc, int field, double currentValue)
+    */
+    public double getDoubleField(PersistenceCapable pc, int field,
+                                 double currentValue) {
+        notNeededByRI("getDoubleField"); // NOI18N
+        return 0.0;
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getStringField(
+    * PersistenceCapable pc, int field, String currentValue)
+    */
+    public String getStringField(PersistenceCapable pc, int field,
+                                 String currentValue) {
+        notNeededByRI("getStringField"); // NOI18N
+        return null;
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#getObjectField(
+    * PersistenceCapable pc, int field, Object currentValue)
+    */
+    public Object getObjectField(PersistenceCapable pc, int field,
+                                 Object currentValue) {
+        notNeededByRI("getObjectField"); // NOI18N
+        return null;
+    }
+
+    private void notNeededByRI(String s) {
+        throw new JDOFatalInternalException(msg.msg(
+            "EXC_NotNeededByRI", s)); // NOI18N
+    }
+    
+    //
+    // setXXXField methods
+    //
+
+    /**
+    * @see javax.jdo.spi.StateManager#setBooleanField(
+    * PersistenceCapable pc, int field, boolean currentValue, boolean newValue)
+    */
+    public void setBooleanField(PersistenceCapable pc, int field,
+                                   boolean currentValue, boolean newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeBooleanField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setCharField(
+    * PersistenceCapable pc, int field, char currentValue, char newValue)
+    */
+    public void setCharField(PersistenceCapable pc, int field,
+                             char currentValue, char newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeCharField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setByteField(
+    * PersistenceCapable pc, int field, byte currentValue, byte newValue)
+    */
+    public void setByteField(PersistenceCapable pc, int field,
+                             byte currentValue, byte newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeByteField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setShortField(
+    * PersistenceCapable pc, int field, short currentValue, short newValue)
+    */
+    public void setShortField(PersistenceCapable pc, int field,
+                               short currentValue, short newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeShortField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setIntField(
+    * PersistenceCapable pc, int field, int currentValue, int newValue)
+    */
+    public void setIntField(PersistenceCapable pc, int field,
+                           int currentValue, int newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeIntField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setLongField(
+    * PersistenceCapable pc, int field, long currentValue, long newValue)
+    */
+    public void setLongField(PersistenceCapable pc, int field,
+                             long currentValue, long newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeLongField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setFloatField(
+    * PersistenceCapable pc, int field, float currentValue, float newValue)
+    */
+    public void setFloatField(PersistenceCapable pc, int field,
+                               float currentValue, float newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeFloatField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setDoubleField(
+    * PersistenceCapable pc, int field, double currentValue, double newValue)
+    */
+    public void setDoubleField(PersistenceCapable pc, int field,
+                                 double currentValue, double newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeDoubleField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setStringField(
+    * PersistenceCapable pc, int field, String currentValue, String newValue)
+    */
+    public void setStringField(PersistenceCapable pc, int field,
+                                 String currentValue, String newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeStringField(field, newValue);
+        prepareSetField(pc, field, sfm);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#setObjectField(
+    * PersistenceCapable pc, int field, Object currentValue, Object newValue)
+    */
+    public void setObjectField(PersistenceCapable pc, int field,
+                                 Object currentValue, Object newValue) {
+        StateFieldManager sfm = new StateFieldManager();
+        sfm.storeObjectField(field, newValue);
+        prepareSetField(pc, field, sfm, currentValue, newValue);
+    }
+
+    
+    //
+    // providedXXXField methods
+    //
+    
+    /** 
+    * @see javax.jdo.spi.StateManager#providedBooleanField(
+    * PersistenceCapable pc, int field, boolean currentValue)
+    */    
+    public void providedBooleanField(PersistenceCapable pc, int field,
+                                     boolean currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeBooleanField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedCharField(
+    * PersistenceCapable pc, int field, char currentValue)
+    */    
+    public void providedCharField(PersistenceCapable pc, int field,
+                                  char currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeCharField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedByteField(
+    * PersistenceCapable pc, int field, byte currentValue)
+    */    
+    public void providedByteField(PersistenceCapable pc, int field,
+                                  byte currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeByteField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedShortField(
+    * PersistenceCapable pc, int field, short currentValue)
+    */    
+    public void providedShortField(PersistenceCapable pc, int field,
+                                   short currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeShortField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedIntField(
+    * PersistenceCapable pc, int field, int currentValue)
+    */    
+    public void providedIntField(PersistenceCapable pc, int field,
+                                 int currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeIntField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedLongField(
+    * PersistenceCapable pc, int field, long currentValue)
+    */    
+    public void providedLongField(PersistenceCapable pc, int field,
+                                  long currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeLongField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedFloatField(
+    * PersistenceCapable pc, int field, float currentValue)
+    */    
+    public void providedFloatField(PersistenceCapable pc, int field,
+                                   float currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeFloatField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedDoubleField(
+    * PersistenceCapable pc, int field, double currentValue)
+    */    
+    public void providedDoubleField(PersistenceCapable pc, int field,
+                                    double currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeDoubleField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedStringField(
+    * PersistenceCapable pc, int field, String currentValue)
+    */    
+    public void providedStringField(PersistenceCapable pc, int field,
+                                    String currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeStringField(field, currentValue);
+        }
+    }
+
+    /** 
+    * @see javax.jdo.spi.StateManager#providedObjectField(
+    * PersistenceCapable pc, int field, Object currentValue)
+    */    
+    public void providedObjectField(PersistenceCapable pc, int field,
+                                    Object currentValue) {
+        if (verifyProvider(pc)) {
+            fieldManager.storeObjectField(field, currentValue);
+        }
+    }
+
+    
+    //
+    // replacingXXXField methods
+    //
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingBooleanField(
+    * PersistenceCapable pc, int field)
+    */
+    public boolean replacingBooleanField(PersistenceCapable pc, int field) {
+        loadingField(pc, field); 
+        return fieldManager.fetchBooleanField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingCharField(
+    * PersistenceCapable pc, int field)
+    */
+    public char replacingCharField(PersistenceCapable pc, int field) {
+        loadingField(pc, field); 
+        return fieldManager.fetchCharField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingByteField(
+    * PersistenceCapable pc, int field)
+    */
+    public byte replacingByteField(PersistenceCapable pc, int field) {
+        loadingField(pc, field);
+        return fieldManager.fetchByteField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingShortField(
+    * PersistenceCapable pc, int field)
+    */
+    public short replacingShortField(PersistenceCapable pc, int field) {
+        loadingField(pc, field);
+        return fieldManager.fetchShortField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingIntField(
+    * PersistenceCapable pc, int field)
+    */
+    public int replacingIntField(PersistenceCapable pc, int field) {
+        loadingField(pc, field);
+        return fieldManager.fetchIntField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingLongField(
+    * PersistenceCapable pc, int field)
+    */
+    public long replacingLongField(PersistenceCapable pc, int field) {
+        loadingField(pc, field);
+        return fieldManager.fetchLongField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingFloatField(
+    * PersistenceCapable pc, int field)
+    */
+    public float replacingFloatField(PersistenceCapable pc, int field) {
+        loadingField(pc, field);
+        return fieldManager.fetchFloatField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingDoubleField(
+    * PersistenceCapable pc, int field)
+    */
+    public double replacingDoubleField(PersistenceCapable pc, int field) {
+        loadingField(pc, field);
+        return fieldManager.fetchDoubleField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingStringField(
+    * PersistenceCapable pc, int field)
+    */
+    public String replacingStringField(PersistenceCapable pc, int field) {
+        loadingField(pc, field);
+        return fieldManager.fetchStringField(field);
+    }
+
+    /**
+    * @see javax.jdo.spi.StateManager#replacingObjectField(
+    * PersistenceCapable pc, int field)
+    */
+    public Object replacingObjectField(PersistenceCapable pc, int field) {
+        loadingField(pc, field);
+        return fieldManager.fetchObjectField(field);
+    }
+
+    //
+    // Implemention of other StateManagerInternal methods
+    //
+
+    /**
+     * Returns true if current state is present in the datastore.
+     */
+    public boolean isStored() {
+        return myLC.isStored();
+    }
+
+    /**
+     * Returns true if current state is flushed.
+     */
+    public boolean isFlushed() {
+        return myLC.isFlushed();
+    }
+
+    /**
+     * @see org.apache.jdo.state.StateManagerInternal#setDependency(
+     * Object dependency)
+     */
+    public Object setDependency(Object dependency) {
+        return this.dependency = dependency;
+    }
+
+    /**
+     * @see org.apache.jdo.state.StateManagerInternal#getDependency()
+     */
+    public Object getDependency() {
+        return dependency;
+    }
+    /**
+    * @see org.apache.jdo.state.StateManagerInternal#getObject()
+    */
+    public PersistenceCapable getObject() {
+        return myPC;
+    }
+
+    /**
+    * @see org.apache.jdo.state.StateManagerInternal#setObjectId(Object objectId)
+    */
+    public void setObjectId(Object objectId) {
+        myPM.replaceObjectId(this.objectId, objectId);
+        this.objectId = objectId;
+        // RESOLVE: what will happen if we support PK updates?
+        this.txObjectId = objectId;
+    }
+
+    /** Return the object representing the JDO identity
+    * of the associated instance
+    * @return the object representing the JDO identity of the associated
+    * instance.
+    */
+    public Object getInternalObjectId () {
+        if (null == objectId) {
+            if (debugging())
+                debug("getInternalObjectId"); // NOI18N
+
+            StoreManager srm = myPM.getStoreManager();
+            objectId = srm.createObjectId(this, myPM);
+            txObjectId = this.objectId;
+        }
+        return objectId;
+    }
+
+    /** Returns external representation of the object id that can be used
+     * by the client
+     */
+    public Object getExternalObjectId () {
+        StoreManager srm = myPM.getStoreManager();
+        return srm.getExternalObjectId(objectId, myPC);
+    }
+
+    /**
+    * @see org.apache.jdo.state.StateManagerInternal#provideField(
+    * int fieldNumber,
+    * FieldManager fieldManager, boolean identifying)
+    */
+    // Synchronized to avoid conflicts w.r.t. fieldManager.
+    public synchronized void provideField(int fieldNumber,
+                                          FieldManager fieldManager,
+                                          boolean identifying) {
+
+        this.fieldManager = fieldManager; // Save for callback in giveXXXField
+        if (identifying) {
+            if (flushedImage != null) {
+                expectedProvider = flushedImage; // Save for verification.
+                flushedImage.jdoProvideField(fieldNumber);
+            } else {
+                expectedProvider = beforeImage; // Save for verification.
+                beforeImage.jdoProvideField(fieldNumber); 
+            }
+
+        } else {
+            expectedProvider = myPC; // Save for verification.
+            myPC.jdoProvideField(fieldNumber);
+        }
+        expectedProvider = null; // No expected request.
+    }
+
+    /**
+    * @see org.apache.jdo.state.StateManagerInternal#provideFields(int[] fields,
+    * FieldManager fieldManager, boolean identifying)
+    */
+    // Synchronized to avoid conflicts w.r.t. fieldManager.
+    public synchronized void provideFields(int fields[],
+                                           FieldManager fieldManager,
+                                           boolean identifying) {
+
+        this.fieldManager = fieldManager; // Save for callback in giveXXXField
+        if (identifying) {
+            if (flushedImage != null) {
+                expectedProvider = flushedImage; // Save for verification.
+                flushedImage.jdoProvideFields(fields);
+            } else {
+                expectedProvider = beforeImage; // Save for verification.
+                beforeImage.jdoProvideFields(fields); 
+            }
+
+        } else {
+            expectedProvider = myPC; // Save for verification.
+            myPC.jdoProvideFields(fields);
+        }
+        expectedProvider = null; // No expected request.
+    }
+
+    /**
+     * @see org.apache.jdo.state.StateManagerInternal#replaceFields(int[] fields,
+     * FieldManager fieldManager)
+     */
+     // Synchronized to avoid conflicts w.r.t. fieldManager.
+    public synchronized void replaceFields(int[] fields,
+                                           FieldManager fieldManager) {
+        this.fieldManager = fieldManager; // Save for callback in giveXXXField
+        if (fields != null && fields.length > 0) {
+            expectedProvider = myPC; // Save for verification.
+            myPC.jdoReplaceFields(fields);
+            expectedProvider = null; // No expected request.
+
+            // Merge BeforeImage
+            updateBeforeImage(fields);
+        }
+
+        this.fieldManager = null;
+    }
+     
+    /**
+    * For replacing field values in a PC with one that is provided by 
+    * the FieldManager. This method does not replace fields that are
+    * already loaded, even if their field number are included in the
+    * specified field number array.
+    * @param fields Indicates which fields should be replaced in the PC.
+    * @param fieldManager FieldManager from which the field values should
+    * be obtained.
+    */
+    protected void replaceUnloadedFields(int[] fields,
+                                         FieldManager fieldManager) {
+        replaceFields(getUnloaded(fields, loadedFields), fieldManager);
+    }
+    
+    /*
+     * @see org.apache.jdo.state.StateManagerInternal#preStore()
+     */
+    public void preStore() {
+        if (myLC.isDeleted) {
+            // Don't call jdoPreStore for deleted instances.
+            return;
+        } else if (javax.jdo.InstanceCallbacks.class.isInstance(myPC)) {
+            ((InstanceCallbacks)myPC).jdoPreStore();
+        }
+
+    }
+
+    /**
+     * Tracing method
+     * @param msg String to display
+     */  
+    private void debug(String msg) {
+        logger.debug("In StateManagerImpl " + msg); // NOI18N
+    }
+
+    /**
+     * Verifies if debugging is enabled.
+     * @return true if debugging is enabled.
+     */
+    private boolean debugging() {
+        return logger.isDebugEnabled();
+    }
+}