Package org.exolab.castor.persist

Source Code of org.exolab.castor.persist.CollectionProxy$MapProxy

/**
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
*    statements and notices.  Redistributions must also contain a
*    copy of this document.
*
* 2. Redistributions in binary form must reproduce the
*    above copyright notice, this list of conditions and the
*    following disclaimer in the documentation and/or other
*    materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
*    products derived from this Software without prior written
*    permission of Intalio, Inc.  For written permission,
*    please contact info@exolab.org.
*
* 4. Products derived from this Software may not be called "Exolab"
*    nor may "Exolab" appear in their names without prior written
*    permission of Intalio, Inc. Exolab is a registered
*    trademark of Intalio, Inc.
*
* 5. Due credit should be given to the Exolab Project
*    (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
* INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 1999 (C) Intalio, Inc. All Rights Reserved.
*
* $Id: ClassMolder.java,v 1.4 2003/04/16 21:51:32 mguessan Exp $
*/


package org.exolab.castor.persist;


import java.math.BigDecimal;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OptionalDataException;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Collection;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import org.exolab.castor.jdo.Persistent;

import org.exolab.castor.jdo.TimeStampable;
import org.exolab.castor.jdo.ObjectNotFoundException;
import org.exolab.castor.jdo.ClassNotPersistenceCapableException;
import org.exolab.castor.jdo.DuplicateIdentityException;
import org.exolab.castor.jdo.ObjectDeletedException;
import org.exolab.castor.jdo.ObjectNotFoundException;
import org.exolab.castor.jdo.ObjectModifiedException;
import org.exolab.castor.jdo.PersistenceException;
import org.exolab.castor.jdo.engine.SQLEngine;
import org.exolab.castor.jdo.engine.JDOCallback;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.mapping.TypeConvertor;
import org.exolab.castor.mapping.loader.Types;
import org.exolab.castor.mapping.loader.FieldHandlerImpl;
import org.exolab.castor.mapping.xml.ClassMapping;
import org.exolab.castor.mapping.xml.FieldMapping;
import org.exolab.castor.mapping.loader.MappingLoader;
import org.exolab.castor.persist.spi.Persistence;
import org.exolab.castor.persist.spi.PersistenceQuery;
import org.exolab.castor.mapping.FieldDescriptor;
import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.mapping.AccessMode;
import org.exolab.castor.persist.spi.CallbackInterceptor;
import org.exolab.castor.persist.spi.PersistenceFactory;
import org.exolab.castor.persist.spi.LogInterceptor;
import org.exolab.castor.mapping.loader.ClassDescriptorImpl;
import org.exolab.castor.jdo.engine.JDOClassDescriptor;
import org.exolab.castor.jdo.engine.JDOFieldDescriptor;
import org.exolab.castor.persist.spi.Complex;
import org.exolab.castor.util.Messages;
import java.sql.Connection;


/**
* ClassMolder is a binder for one type of data object and its
* {@link Persistence}. For example, when ClassMolder is asked to load
* an object, it acquires values from {@link Persistence} and binds it
* into an target object. It also resolves relations via
* {@link TransactionContext} and bind related object into an target object.
* <p>
* Beside loading, ClassMolder is also repsonsible for storing, removing,
* creating an object to and from a persistence storage, as well as
* reverting an object to previous state.
* <p>
* Each instance of ClassMolder deals with exactly one persistable type,
* interacts with one instance of Persistent and belongs to one
* {@link LockEngine}.
* <p>
*
*
* @author <a href="yip@intalio.com">Thomas Yip</a>
*/

public class ClassMolder {

    /**
     * The fully qualified name of the java data object class which this ClassMolder
     * corresponds to. We call it base class.
     */
    private String _name;

    /**
     * <tt>FieldMolder</tt>s of the identity of the base class
     */
    private FieldMolder[] _ids;

    /**
     * <tt>FieldMolder</tt>s of the fields of the base class
     */
    private FieldMolder[] _fhs;

    /**
     * <tt>ClassMolder</tt> of the java data object class's ClassMolder which
     * the base class extends.
     */
    private ClassMolder _extends;

    /**
     * <tt>ClassMolder</tt> of the java data object class's ClassMolder which
     * the base class depends on. <tt>null</tt> if it class is an indenpendent
     * class.
     */
    private ClassMolder _depends;

    /**
     * A Vector of <tt>ClassMolder</tt>s for all the direct dependent class of the
     * base class.
     */
    private Vector _dependent;

    /**
     * A Vector of <tt>ClassMolder</tt>s for all the direct extending class of the
     * base class.
     */
    private Vector _extendent;

    /**
     * Default accessMode of the base class.
     */
    private AccessMode _accessMode;

    /**
     * The Persistent of the base class.
     */
    private Persistence _persistence;

    /**
     * The LockEngine which this instace of ClassMolder belong to.
     */
    private LockEngine _engine;

    /**
     * The CallbackInterceptor for the base class.
     */
    private CallbackInterceptor _callback;

    /**
     * The LRU mechanism to be used for caching freed instance of the base class.
     */
    private int _cachetype;

    /**
     * The LRU parameter(or capcity) to be used for caching freed instance of the
     * base class.
     */
    private int _cacheparam;

    /**
     * Is a key kenerator used for the base class?
     */
    private boolean _isKeyGenUsed;

    /**
     * True if org.exolab.castor.debug="true"
     */
    private boolean _debug;


    /**
     * True if the representing class implements the interface TimeStampable
     */
    private boolean _timeStampable;

    /**
     * Create priority
     */
    private int     _priority = -1;

    /**
     * Constructor
     *
     * @param ds      is the helper class for resolving depends and extends relationship
     *                among all the ClassMolder in the same LockEngine.
     * @param loader  is the mapping loader
     * @param classDescriptor   the classDescriptor for the base class
     * @param persist the Persistent for the base class
     */
    ClassMolder( DatingService ds, MappingLoader loader, LockEngine lock,
            ClassDescriptor clsDesc, Persistence persist )
            throws ClassNotFoundException, MappingException {

        _debug = Boolean.getBoolean("org.exolab.castor.debug");

        ClassMapping clsMap = ((ClassDescriptorImpl) clsDesc).getMapping();

        _engine = lock;

        _persistence = persist;

        _name = clsMap.getName();

        _accessMode = AccessMode.getAccessMode( clsMap.getAccess().toString() );

        _timeStampable = TimeStampable.class.isAssignableFrom( clsDesc.getJavaClass() );

        ds.register( _name, this );

        ClassMapping dep = (ClassMapping) clsMap.getDepends();
        ClassMapping ext = (ClassMapping) clsMap.getExtends();

        //if ( dep != null && ext != null )
        //    throw new MappingException("A JDO cannot both extends and depends on other objects");

        if ( dep != null ) {
            ds.pairDepends( this, dep.getName() );
        }

        if ( ext != null ) {
            ds.pairExtends( this, ext.getName() );
        }

        if ( clsDesc instanceof JDOClassDescriptor ) {
            if ( ((JDOClassDescriptor) clsDesc).getCacheType() != null ) {
                _cachetype = LRU.mapType( ((JDOClassDescriptor) clsDesc).getCacheType() );
                _cacheparam = ((JDOClassDescriptor) clsDesc).getCacheParam();
            }
            _isKeyGenUsed = ( ( (JDOClassDescriptor) clsDesc ).getKeyGeneratorDescriptor() != null );
        }

        // construct <tt>FieldModler</tt>s for each of the identity fields of
        // the base class.
        FieldMapping[] fmId = getIdFields( clsMap );
        _ids = new FieldMolder[fmId.length];
        for ( int i=0; i<_ids.length; i++ ) {
            _ids[i] = new FieldMolder( ds, this, fmId[i] );
        }

        // construct <tt>FieldModlers</tt>s for each of the fields of the base
        // class.
        FieldMapping[] fmFields = getFullFields( clsMap );
        _fhs = new FieldMolder[fmFields.length];
        for ( int i=0; i<_fhs.length; i++ ) {
            if ( fmFields[i].getSql() != null && fmFields[i].getSql().getManyTable() != null ) {
                // the fields is not primitive
                String manyTable = null;
                String[] idSQL = null;
                int[] idType = null;
                String[] relatedIdSQL = null;
                int[] relatedIdType = null;
                TypeConvertor[] idConvertTo = null;
                TypeConvertor[] idConvertFrom = null;
                String[] idConvertParam = null;
                TypeConvertor[] relatedIdConvertTo = null;
                TypeConvertor[] relatedIdConvertFrom = null;
                String[] relatedIdConvertParam = null;

                manyTable = fmFields[i].getSql().getManyTable();

                idSQL = new String[fmId.length];
                idType = new int[fmId.length];
                idConvertFrom = new TypeConvertor[fmId.length];
                idConvertTo = new TypeConvertor[fmId.length];
                idConvertParam = new String[fmId.length];
                FieldDescriptor[] fd = ((ClassDescriptorImpl)clsDesc).getIdentities();
                for ( int j=0; j < fmId.length; j++ ) {
                    idSQL[j] = fmId[j].getSql().getName()[0];

                    if ( fd[j] instanceof JDOFieldDescriptor ) {
                        int[] type = ((JDOFieldDescriptor)fd[j]).getSQLType();
                        idType[j] = type==null? 0: type[0];
                        FieldHandlerImpl fh = (FieldHandlerImpl)fd[j].getHandler();
                        idConvertTo[j] = fh.getConvertTo();
                        idConvertFrom[j] = fh.getConvertFrom();
                        idConvertParam[j] = fh.getConvertParam();
                    } else {
                        throw new MappingException("Identity type must contains sql information: "+ _name );
                    }
                }

                relatedIdSQL = null;
                String relatedType = fmFields[i].getType();
                ClassDescriptor relDesc = loader.getDescriptor( ds.resolve( relatedType ) );
                if ( relDesc instanceof JDOClassDescriptor ) {
                    FieldDescriptor[] relatedIds = ((JDOClassDescriptor)relDesc).getIdentities();
                    relatedIdSQL = new String[relatedIds.length];
                    relatedIdType = new int[relatedIds.length];
                    relatedIdConvertTo = new TypeConvertor[relatedIds.length];
                    relatedIdConvertFrom = new TypeConvertor[relatedIds.length];
                    relatedIdConvertParam = new String[relatedIds.length];
                    for ( int j=0; j < relatedIdSQL.length; j++ ) {
                        if ( relatedIds[j] instanceof JDOFieldDescriptor ) {
                            String[] tempId = ((JDOFieldDescriptor)relatedIds[j]).getSQLName();
                            relatedIdSQL[j] = tempId==null? null: tempId[0];
                            int[] tempType = ((JDOFieldDescriptor)relatedIds[j]).getSQLType();
                            relatedIdType[j] = tempType==null? 0: tempType[0];
                            FieldHandlerImpl fh = (FieldHandlerImpl)relatedIds[j].getHandler();
                            relatedIdConvertTo[j] = fh.getConvertTo();
                            relatedIdConvertFrom[j] = fh.getConvertFrom();
                            relatedIdConvertParam[j] = fh.getConvertParam();
                        } else {
                            throw new MappingException("Field type is not persistence-capable: "+ relatedIds[j].getFieldName() );
                        }
                    }
                }

                // if many-key exist, idSQL is overridden
                String[] manyKey = fmFields[i].getSql().getManyKey();
                if ( manyKey != null && manyKey.length != 0 ) {
                    if ( manyKey.length != idSQL.length )
                        throw new MappingException("The number of many-keys doesn't match referred object: "+clsDesc.getJavaClass().getName());
                    idSQL = manyKey;
                }

                // if name="" exist, relatedIdSQL is overridden
                String[] manyName = fmFields[i].getSql().getName();
                if ( manyName != null && manyName.length != 0 ) {
                    if ( manyName.length != relatedIdSQL.length )
                        throw new MappingException("The number of many-keys doesn't match referred object: "+relDesc.getJavaClass().getName());
                    relatedIdSQL = manyName;
                }

                _fhs[i] = new FieldMolder( ds, this, fmFields[i], manyTable,
                        idSQL, idType, idConvertTo, idConvertFrom, idConvertParam,
                        relatedIdSQL, relatedIdType, relatedIdConvertTo, relatedIdConvertFrom, relatedIdConvertParam );
            } else {
                _fhs[i] = new FieldMolder( ds, this, fmFields[i] );
            }
        }

        // ssa, FIXME : Are the two statements equivalents ?
        //        if ( Persistent.class.isAssignableFrom( _base ) )
        if ( Persistent.class.isAssignableFrom( ds.resolve(_name) ) )
            _callback = new JDOCallback();
    }

    /*
     * Get the all the id fields of a class
     * If the class, C, is a dependent class, then
     * the depended class', D, id fields will be
     * appended at the back and returned.
     * If the class is an extended class, the id
     * fields of the extended class will be returned.
     */
    private FieldMapping[] getIdFields( ClassMapping clsMap )
            throws MappingException {
        ClassMapping base;
        FieldMapping[] fmDepended;
        FieldMapping[] fmResult;
        FieldMapping[] fmBase;
        FieldMapping[] fmIds;
        String[] identities;

        // start with the extended class
        base = clsMap;
        while ( base.getExtends() != null ) {
            base = (ClassMapping) base.getExtends();
        }
        fmDepended = null;

        identities = base.getIdentity();

        if ( identities == null || identities.length == 0 )
            throw new MappingException("Identity is null!");


        fmIds = new FieldMapping[identities.length];
        fmBase = base.getFieldMapping();
        for ( int i=0,j=0; i<fmBase.length; i++ ) {
            for ( int k=0; k<identities.length; k++ ) {
                if ( fmBase[i].getName().equals( identities[k] ) ) {
                    fmIds[k] = fmBase[i];
                    break;
                }
            }
        }
        if ( fmDepended == null )
            return fmIds;

        // join depend ids and class id
        fmResult = new FieldMapping[fmDepended.length + identities.length];
        System.arraycopy( fmIds, 0, fmResult, 0, fmIds.length );
        System.arraycopy( fmDepended, 0 , fmResult, fmIds.length, fmDepended.length );
        return fmIds;
    }

    /*
     * Get all the field mapping, including all the field
     * in extended class, but id fields
     */
    private FieldMapping[] getFullFields( ClassMapping clsMap )
            throws MappingException {
        FieldMapping[] extendFields;
        FieldMapping[] thisFields;
        FieldMapping[] fields = null;
        String[] identities;
        boolean idfield;
        ClassMapping extend = (ClassMapping) clsMap.getExtends();
        ClassMapping origin;
        ArrayList fieldList;

        if ( extend != null ) {
            origin = extend;
            while (origin.getExtends() != null) {
                origin = (ClassMapping) origin.getExtends();
            }
            identities = origin.getIdentity();
            extendFields = getFullFields( extend );
            thisFields = clsMap.getFieldMapping();

            fieldList = new ArrayList(extendFields.length + thisFields.length);
            for (int i = 0; i < extendFields.length; i++) {
                fieldList.add(extendFields[i]);
            }
            for ( int i=0; i<thisFields.length; i++ ) {
                for ( int k=0; k<identities.length; k++ ) {
                    if ( ! thisFields[i].getName().equals( identities[k] ) ) {
                        fieldList.add(thisFields[i]);
                        break;
                    }
                }
            }
            fields = new FieldMapping[fieldList.size()];
            fieldList.toArray(fields);
        } else {
            identities = clsMap.getIdentity();
            if ( identities == null || identities.length == 0 )
                throw new MappingException("Identity is null!");

            // return array of fieldmapping without the id field
            thisFields = clsMap.getFieldMapping();
            fields = new FieldMapping[thisFields.length-identities.length];

            for ( int i=0,j=0; i<thisFields.length; i++ ) {
                idfield = false;
                IDSEARCH:
                for ( int k=0; k<identities.length; k++ ) {
                    if ( thisFields[i].getName().equals( identities[k] ) ) {
                        idfield = true;
                        break IDSEARCH;
                    }
                }
                if ( !idfield ) {
                    fields[j] = thisFields[i];
                    j++;
                }
            }

        }
        return fields;
    }

    /**
     * Resolve and construct all the <tt>ClassMolder</tt>s given a MappingLoader.
     *
     * @param   loader    MappingLoader for the LockEngine
     * @param   lock      LockEngine for all the ClassMolder
     * @param   factory   factory class for getting Persistent of the ClassMolder
     *
     * @return  Vector of all of the <tt>ClassMolder</tt>s from a MappingLoader
     */
    public static Vector resolve( MappingLoader loader, LockEngine lock,
            PersistenceFactory factory, LogInterceptor logInterceptor )
            throws MappingException, ClassNotFoundException {

        Vector result = new Vector();
        Enumeration enum;
        ClassMolder mold;
        ClassMapping map;
        Persistence persist;
        ClassDescriptor desc;

        DatingService ds = new DatingService( loader.getClassLoader() );

        enum = loader.listJavaClasses();
        while ( enum.hasMoreElements() ) {
            desc = (ClassDescriptor) loader.getDescriptor((Class)enum.nextElement());
            persist = factory.getPersistence( desc, logInterceptor );
            mold = new ClassMolder( ds, loader, lock, desc, persist );
            result.add( mold );
        }
        ds.close();
        return result;
    }

    /**
     * Remove the reference of a related object from an object of
     * the base class. <p>
     *
     * If the related object is PersistanceCapable, the field will
     * be set null. If the related object is a Collection, then
     * the related object will be removed from the Collection. <p>
     *
     * If any changed occured, transactionContext.markModified
     * will be called, to indicate the object is modified. <p>
     *
     * It method will iterate thur all of the object's field and
     * try to remove all the occurrence.
     *
     * @param tx      the TransactionContext of the transaction in action
     * @param object  the target object of the base type of this ClassMolder
     * @param relatedMolder  the ClassMolder of the related object to be
     *                       removed from the object
     * @param related        the object to be removed
     */
    private boolean removeRelation( TransactionContext tx, Object object,
            ClassMolder relatedMolder, Object relatedObject )  {

        boolean removed = false;
        boolean updateCache = false;
        boolean updatePersist = false;
        ClassMolder relatedBaseMolder = null;
        for ( int i=0; i < _fhs.length; i++ ) {
            int fieldType = _fhs[i].getFieldType();
            ClassMolder fieldClassMolder;
            switch (fieldType) {
            case FieldMolder.PERSISTANCECAPABLE:
                // de-reference the object
                fieldClassMolder = _fhs[i].getFieldClassMolder();

                relatedBaseMolder = relatedMolder;
                while ( fieldClassMolder != relatedBaseMolder && relatedBaseMolder != null ) {
                    relatedBaseMolder = relatedBaseMolder._extends;
                }
                if ( fieldClassMolder == relatedBaseMolder ) {
                    Object related = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( related == relatedObject ) {
                        _fhs[i].setValue( object, null, tx.getClassLoader() );
                        updateCache = true;
                        updatePersist = true;
                        removed = true;
                    }
                    // |
                }
                break;
            case FieldMolder.ONE_TO_MANY:
            case FieldMolder.MANY_TO_MANY:
                // remove the object from the collection
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                relatedBaseMolder = relatedMolder;
                while ( fieldClassMolder != relatedBaseMolder && relatedBaseMolder != null ) {
                    relatedBaseMolder = relatedBaseMolder._extends;
                }
                if ( fieldClassMolder == relatedBaseMolder ) {
                    boolean changed = false;
                    Object related = _fhs[i].getValue( object, tx.getClassLoader() );

                    if ( related instanceof RelationCollection ) {
                        RelationCollection lazy = (RelationCollection)related;
                        changed = lazy.remove( relatedObject );
                    } else {
                        Iterator itor = getIterator( related );
                        while ( itor.hasNext() ) {
                            Object o = itor.next();
                            if ( o == relatedObject ) {
                                changed = true;
                                itor.remove();
                            }
                        }
                    }
                    if ( changed ) {
                        updateCache = true;
                        updatePersist = false;
                        removed = true;
                    }
                }
                break;
            }
        }
        tx.markModified( object, updatePersist, updateCache );
        return removed;
    }

    /**
     * Determines the create priority of the data object class represented by
     * this ClassMolder. Concpetually, this method determines the order of
     * which data object should be created. Priority of zero means the object
     * can be created without depending on other data object.
     *
     * This method should only be called after DatingService is closed.
     */
    public int getPriority() {
        if ( _priority == -2 ) {
          // circular reference detected, do not loop
          return 0;
        } else if ( _priority < 0 ) {
            // mark current Molder to avoid infinite loop with circular reference
            _priority = -2;
            int maxPrior = 0;
            for ( int i = 0; i < _fhs.length; i++ ) {
                if ( _fhs[i].isPersistanceCapable()
                        && _fhs[i].getFieldClassMolder() != this
                        && _fhs[i].isStored()
                        && _fhs[i].getFieldClassMolder().isKeyGeneratorUsed() ) {
                    maxPrior = Math.max( maxPrior, _fhs[i].getFieldClassMolder().getPriority()+1 );
                }
                /* should an "if case" for add _ids[i].isForeginKey() in the future */
            }

            /* adjust priority if there are extendent classes */
            if (_extendent != null) {
              for (int i=0;i<_extendent.size();i++) {
                maxPrior = Math.max(maxPrior, ((ClassMolder)_extendent.get(i)).getPriority());
              }
            }

            _priority  = maxPrior;
        }
        return _priority;
    }

    /**
     * Load an object with specified identity from the persistent storage.
     *
     * @param tx   the TransactionContext in action
     * @param oid  the object identity of the desired object
     * @param locker   the {@link DepositBox} of the object which is used to
     *                 store the dirty checking cache of the object.
     * @param accessMode  the acessMode for the object
     * @return the object stamp for the object in the persistent storage
     */
    public Object load( TransactionContext tx, OID oid, DepositBox locker,
            Object object, AccessMode suggestedAccessMode )
            throws ObjectNotFoundException, PersistenceException {
            return load( tx, oid, locker, object, suggestedAccessMode, null );
    }

    public Object load( TransactionContext tx, OID oid, DepositBox locker,
            Object object, AccessMode suggestedAccessMode, QueryResults results )
            throws ObjectNotFoundException, PersistenceException {

        Connection conn;
        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        Object[] fields;
        Object ids;
        Object stamp = null;
        Object temp;
        int fieldType;
        AccessMode accessMode = getAccessMode( suggestedAccessMode );

        if ( oid.getIdentity() == null )
            throw new PersistenceException("The identities of the object to be loaded is null");

        // load the fields from the persistent storage if the cache is empty
        // and the accessMode is readOnly.
        fields = (Object[]) locker.getObject( tx );
        if ( fields == null || accessMode == AccessMode.DbLocked ) {
            fields = new Object[_fhs.length];
            if( results != null ) {
                stamp = results.getQuery().fetch( fields, oid.getIdentity() );
            } else {
                conn = (Connection)tx.getConnection(oid.getLockEngine());
                stamp = _persistence.load( conn, fields, oid.getIdentity(), accessMode );
            }
            oid.setDbLock( accessMode == AccessMode.DbLocked );
            locker.setObject( tx, fields );
        }

        // set the timeStamp of the data object to locker's timestamp
        if ( object instanceof TimeStampable ) {
            ((TimeStampable)object).jdoSetTimeStamp( locker.getTimeStamp() );
        }

        // set the identities into the target object
        ids = oid.getIdentity();
        setIdentity( tx, object, ids );

        // iterates thur all the field of the object and bind all field.
        for ( int i = 0; i < _fhs.length; i++ ) {
            fieldType = _fhs[i].getFieldType();
            switch (fieldType) {
            case FieldMolder.PRIMITIVE:
                // simply set the corresponding Persistent field value into the object
                temp = fields[i];
                if ( temp != null )
                    _fhs[i].setValue( object, temp, tx.getClassLoader() );
                else
                    _fhs[i].setValue( object, null, tx.getClassLoader() );
                break;

            case FieldMolder.SERIALIZABLE:
                // deserialize byte[] into java object
                try {
                    byte[] bytes = (byte[]) fields[i];
                    if ( bytes != null ) {
                        // The following code can be updated, after Blob-->InputStream
                        // to enhance performance.
                        ByteArrayInputStream bis = new ByteArrayInputStream( bytes );
                        ObjectInputStream os = new ObjectInputStream( bis );
                        Object o = os.readObject();
                        _fhs[i].setValue( object, o, tx.getClassLoader() );
                    } else {
                        _fhs[i].setValue( object, null, tx.getClassLoader() );
                    }
                } catch ( OptionalDataException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                } catch ( ClassNotFoundException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                } catch ( IOException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                }
                break;

            case FieldMolder.PERSISTANCECAPABLE:
                // field is not primitive type. Related object will be loaded
                // thru the transaction in action if needed.

                // lazy loading for object (hollow object) is not support in
                // this version. Warns user if lazy loading is specified.
                if ( _fhs[i].isLazy() )
                    System.err.println( "Warning: Lazy loading of object is not yet support!" );

                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();

                if ( fields[i] != null ) {
                    // use the corresponding Persistent fields as the identity,
                    // and we ask transactionContext in action to load it.
                    try {
                        temp = tx.load( fieldEngine, fieldClassMolder, fields[i], null, suggestedAccessMode );
                    } catch (ObjectNotFoundException ex) {
                        temp = null;
                    }
                    _fhs[i].setValue( object, temp, tx.getClassLoader() );
                } else {
                    _fhs[i].setValue( object, null, tx.getClassLoader() );
                }
                break;
            case FieldMolder.ONE_TO_MANY:
            case FieldMolder.MANY_TO_MANY:
                // field is one-to-many and many-to-many type. All the related
                // object will be loaded and put in a Collection. And, the
                // collection will be set as the field.
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();

                if ( !_fhs[i].isLazy() ) {
                    // lazy loading is not specified, load all objects into
                    // the collection and set the Collection as the data object
                    // field.
                    ArrayList v = (ArrayList)fields[i];
                    if ( v != null ) {
                      // simple array type support
                      Class collectionType = _fhs[i].getCollectionType();
                      if (collectionType.isArray()) {
                        Object[] value = (Object[])java.lang.reflect.Array.newInstance(
                                collectionType.getComponentType(), v.size());
                        for ( int j=0,l=v.size(); j<l; j++ ) {
                          value[j] = tx.load( oid.getLockEngine(), fieldClassMolder, v.get(j), null, suggestedAccessMode );
                        }
                        _fhs[i].setValue( object, value, tx.getClassLoader() );
                      } else {
                        CollectionProxy cp = CollectionProxy.create( _fhs[i], object, tx.getClassLoader() );
                        for ( int j=0,l=v.size(); j<l; j++ ) {
                            cp.add( v.get(j), tx.load( oid.getLockEngine(), fieldClassMolder, v.get(j), null, suggestedAccessMode ) );
                        }
                        cp.close();
                        //_fhs[i].setValue( object, cp.getCollection() );
                      }
                    } else {
                        _fhs[i].setValue( object, null, tx.getClassLoader() );
                    }
                } else {
                    // lazy loading is specified. Related object will not be loaded.
                    // A lazy collection with all the identity of the related object
                    // will constructed and set as the data object's field.
                    ArrayList list = (ArrayList) fields[i];
                    RelationCollection relcol = new RelationCollection( tx, oid, fieldEngine, fieldClassMolder, accessMode, list );
                    _fhs[i].setValue( object, relcol, tx.getClassLoader() );
                }
                break;
            default:
                throw new PersistenceException("Unexpected field type!");
            }
        }
        return stamp;
    }

    /**
     * Create an object of the base class with specified identity into the persistence storage.
     *
     * @param tx   transaction in action
     * @param oid  the object identity of the object to be created.
     * @param locker the dirty checking cache of the object
     * @param object  the object to be created
     * @return  the identity of the object
     */
    public Object create( TransactionContext tx, OID oid, DepositBox locker, Object object )
            throws DuplicateIdentityException, PersistenceException {

        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        ArrayList fids;
        Object[] fields;
        Object createdId;
        Object ids;
        Object fid;
        Object o;

        int fieldType;

        if ( _persistence == null )
            throw new PersistenceException("non persistence capable: "+oid.getName());

        fields = new Object[_fhs.length];
        ids = oid.getIdentity();

        // copy the object to cache should make a new field now,
        for ( int i=0; i<_fhs.length; i++ ) {
            fieldType = _fhs[i].getFieldType();

            switch (fieldType) {
            case FieldMolder.PRIMITIVE: // primitive includes int, float, Date, Time etc
                fields[i] = _fhs[i].getValue( object, tx.getClassLoader() );
                break;

            case FieldMolder.PERSISTANCECAPABLE:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                o = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( o != null ) {
                    fid = fieldClassMolder.getIdentity( tx, o );
                    if ( fid != null ) {
                        fields[i] = fid;
                    }
                }
                break;

            case FieldMolder.SERIALIZABLE:
                try {
                    Object dependent = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( dependent != null ) {
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        ObjectOutputStream os = new ObjectOutputStream( bos );
                        os.writeObject( dependent );
                        fields[i] = bos.toByteArray();
                    } else {
                        fields[i] = null;
                    }
                } catch ( IOException e ) {
                    throw new PersistenceException( "Error during serializing dependent object", e );
                }
                break;

            case FieldMolder.ONE_TO_MANY:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                o = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( o != null ) {
                    fids = extractIdentityList( tx, fieldClassMolder, o );
                    fields[i] = fids;
                }
                break;

            case FieldMolder.MANY_TO_MANY:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                o = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( o != null ) {
                    fids = extractIdentityList( tx, fieldClassMolder, o );
                    fields[i] = fids;
                }

                break;

            default:
                throw new IllegalArgumentException("Field type invalid!");
            }
        }

        // ask Persistent to create the object into the persistence storage
        createdId = _persistence.create(tx.getDatabase(), (Connection)tx.getConnection(oid.getLockEngine()),
                fields, ids );

        if ( createdId == null )
            throw new PersistenceException("Identity can't be created!");

        // if the creation succeed, we set the field into the dirty checking
        // cache.
        locker.setObject( tx, fields );
        oid.setDbLock( true );

        // set the new timeStamp into the data object
        if ( object instanceof TimeStampable )
            ((TimeStampable)object).jdoSetTimeStamp( locker.getTimeStamp() );

        // set the identity into the object
        setIdentity( tx, object, createdId );

        // after the creation, we must make sure we add the entry in the
        // relation table for all many-to-many relationship
        for ( int i=0; i<_fhs.length; i++ ) {
            fieldType = _fhs[i].getFieldType();

            switch (fieldType) {
            case FieldMolder.MANY_TO_MANY:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                o = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( o != null ) {
                    fids = extractIdentityList( tx, fieldClassMolder, o );
                    fields[i] = fids;
                    Iterator itor = getIterator( o );
                    while ( itor.hasNext() ) {
                        Object oo = itor.next();
                        if ( tx.isPersistent( oo ) ) {
                            _fhs[i].getRelationLoader().createRelation(
                            (Connection)tx.getConnection(oid.getLockEngine()),
                            createdId, fieldClassMolder.getIdentity( tx, oo ) );
                        }
                    }
                }
            }
        }

        return createdId;
    }


    /**
     * Walk the object model and mark object that should be created.
     *
     * @param tx   transaction in action
     * @param oid  the object identity of the object to be created.
     * @param locker the dirty checking cache of the object
     * @param object  the object to be created
     */
    public void markCreate( TransactionContext tx, OID oid, DepositBox locker, Object object )
            throws DuplicateIdentityException, PersistenceException {

        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        boolean updateCache = false;
        Object createdId;
        Object ids;
        Object o;

        // iterate all the fields and mark all the dependent object.
        for ( int i=0; i<_fhs.length; i++ ) {
            int fieldType = _fhs[i].getFieldType();
            switch (fieldType) {
            case FieldMolder.PRIMITIVE:
            case FieldMolder.SERIALIZABLE:
                // nothing need to be done here for primitive
                break;

            case FieldMolder.PERSISTANCECAPABLE:
                // create dependent object if exists
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                o = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( o != null ) {
                    if ( _fhs[i].isDependent() ) {
                        if ( !tx.isRecorded( o ) ) {
                            tx.markCreate( fieldEngine, fieldClassMolder, o, oid );
                            if ( !_fhs[i].isStored() && fieldClassMolder._isKeyGenUsed ) {
                                updateCache = true;
                            }
                        } else {
                            // fail-fast principle: if the object depend on another object,
                            // throw exception
                            if ( !tx.isDepended( oid, o ) )
                                throw new PersistenceException(
                                "Dependent object may not change its master. Object: "+o+" new master: "+oid);
                        }
                    } else if ( tx.isAutoStore() ) {
                        if ( !tx.isRecorded( o ) ) {
                            tx.markCreate( fieldEngine, fieldClassMolder, o, null );
                            if ( !_fhs[i].isStored() && fieldClassMolder._isKeyGenUsed ) {
                                updateCache = true;
                            }
                        }
                    }
                }
                break;

            case FieldMolder.ONE_TO_MANY:
                // create dependent objects if exists
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                o = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( o != null ) {
                    Iterator itor = getIterator( o );
                    while (itor.hasNext()) {
                        Object oo = itor.next();
                        if ( _fhs[i].isDependent() ) {
                            if ( !tx.isRecorded( oo ) ) {
                                //autoCreated = true;
                                tx.markCreate( fieldEngine, fieldClassMolder, oo, oid );
                                if ( fieldClassMolder._isKeyGenUsed )
                                    updateCache = true;
                            } else
                                // fail-fast principle: if the object depend on another object,
                                // throw exception
                                if ( !tx.isDepended( oid, oo ) )
                                    throw new PersistenceException("Dependent object may not change its master");
                        } else if ( tx.isAutoStore() ) {
                            if ( !tx.isRecorded( oo ) ) {
                                tx.markCreate( fieldEngine, fieldClassMolder, oo, null );
                                if ( fieldClassMolder._isKeyGenUsed )
                                    updateCache = true;
                            }
                        }
                    }
                }
                break;

            case FieldMolder.MANY_TO_MANY:
                // create relation if the relation table
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                o = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( o != null ) {
                    Iterator itor = getIterator( o );
                    // many-to-many relation is never dependent relation
                    while (itor.hasNext()) {
                        Object oo = itor.next();
                        if ( tx.isAutoStore() && !tx.isRecorded( oo ) ) {
                            tx.markCreate( fieldEngine, fieldClassMolder, oo, null );
                            updateCache = true;
                        }
                    }
                }
                break;
            }
        }

        tx.markModified( object, false, updateCache );

    }

    private boolean isEquals( Collection c1, Collection c2 ) {

        if ( c1 == c2 )
            return true;
        if ( c1 == null || c2 == null )
            return false;
        if ( c1.containsAll( c2 ) && c2.containsAll( c1 ) )
            return true;
        return false;
    }

    /**
     * A utility method which compare object.
     */
    private static boolean isEquals( Object o1, Object o2 ) {
        if ( o1 == o2 )
            return true;
        if ( o1 == null || o2 == null )
            return false;
        if ( o1.equals( o2 ) )
            return true;
        // [oleg] is some special cases equals doesn't work properly
        if ( (o1 instanceof java.math.BigDecimal) && ((java.math.BigDecimal) o1).compareTo(o2) == 0) {
            return true;
        }
        if ((o1 instanceof java.sql.Timestamp) && (o2 instanceof java.sql.Timestamp)) {
            java.sql.Timestamp t1 = (java.sql.Timestamp) o1;
            java.sql.Timestamp t2 = (java.sql.Timestamp) o2;
            return (t1.getTime() == t2.getTime() && t1.getNanos() / 1000000 == t2.getNanos() / 1000000);
        }

        if((o1 instanceof byte[]) && (o2 instanceof byte[])) {
            return Arrays.equals((byte[]) o1, (byte[]) o2);
        }
        if((o1 instanceof char[]) && (o2 instanceof char[])) {
            return Arrays.equals((char[]) o1, (char[]) o2);
        }
        return false;
    }

    /**
     * Check the object for modification. If dpendent object is dereferenced, it
     * method will remove the object thru the transaction. If an related object
     * is dereferenced, it method will make sure the formally object will be
     * dereferenced from the other side as well.
     *
     * This method is called in prepare (for commit) state of the transaction.
     * This method indicates if the object needed to be persist or cache should
     * be update using TransactionContext.markDelete.
     *
     * @param tx transaction in action
     * @param oid the object identity of the object
     * @param locker the dirty check cache for the object
     * @param object the data object to be checked
     * @param timeout  timeout of updating the lock if needed
     *
     * @return true if the object is modified
     */
    public boolean preStore( TransactionContext tx, OID oid, DepositBox locker, Object object, int timeout )
            throws PersistenceException {

        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        Object[] newfields;
        Object[] fields;
        Object value;
        Iterator itor;
        ArrayList list;
        ArrayList orgFields;
        int fieldType;

        if ( oid.getIdentity() == null )
            throw new PersistenceException("The identity of the object to be stored is null");

        if ( !oid.getIdentity().equals( getIdentity( tx, object ) ) )
            throw new PersistenceException( Messages.format(
                "jdo.identityChanged", _name, oid.getIdentity(),
                getIdentity( tx, object ) ) );

        fields = (Object[]) locker.getObject( tx );

        if ( fields == null )
            throw new PersistenceException("Object, "+oid+",  isn't loaded in the persistence storage!");

        newfields = new Object[_fhs.length];
        boolean updateCache = false;
        boolean updatePersist = false;

        // iterate thru all the data object fields for modification
        for ( int i=0; i<newfields.length; i++ ) {
            fieldType = _fhs[i].getFieldType();
            switch (fieldType) {
            case FieldMolder.PRIMITIVE:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                value =  _fhs[i].getValue( object, tx.getClassLoader() );
                if ( !isEquals( fields[i], value ) ) {
                    if (_fhs[i].isReadonly()) {
                        _fhs[i].setValue( object, fields[i], tx.getClassLoader() );
                    } else {
                        if ( _fhs[i].isStored() /*&& _fhs[i].isCheckDirty()*/ )
                            updatePersist = true;
                        updateCache = true;
                    }
                }
                break;

            case FieldMolder.SERIALIZABLE:
                // deserialize byte[] into java object
                try {
                    byte[] bytes = (byte[]) fields[i];
                    Object fieldValue = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( fieldValue == null && bytes == null) {
                        // do nothing
                    } else if ( fieldValue == null || bytes == null ) {
                        // indicate store is needed
                        if ( _fhs[i].isStored() /*&& _fhs[i].isCheckDirty()*/ )
                            updatePersist = true;
                        updateCache = true;
                    } else { // both not null
                        // The following code can be updated, after Blob-->InputStream
                        // to enhance performance.
                        ByteArrayInputStream bis = new ByteArrayInputStream( bytes );
                        ObjectInputStream os = new ObjectInputStream( bis );
                        Object dependent = os.readObject();
                        if ( !dependent.equals( fieldValue ) ) {
                            if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                                updatePersist = true;
                            updateCache = true;
                        }
                    }
                } catch ( OptionalDataException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                } catch ( ClassNotFoundException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                } catch ( IOException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                }
                break;

            case FieldMolder.PERSISTANCECAPABLE:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                value = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( value != null )
                    newfields[i] = fieldClassMolder.getIdentity( tx, value );

                // | yip: don't delete the following comment,
                //      until it proved working by time. :-P
                // if ids are the same or not canCreate
                //    if object is deleted
                //        warn
                //    if object are the same
                //        done
                //    not the same
                //        exception
                // ids not the same or canCreate
                //    if depend
                //       if old is not null
                //          delete old
                //          removeRelation
                //       if canCreate
                //          create new
                //    not depend and autoStore
                //       if old is not null
                //          removeRelation
                //       if canCreate
                //          createObject
                //    not depend nor autoStore
                //       if old is not null
                //          removeRelation
                //       if new is not null
                if ( isEquals( fields[i], newfields[i] ) ) {
                    if ( !_debug )
                        break;

                    if ( fields[i] == null )
                        break; // do the next field if both are null

                    if ( value != null && tx.isDeleted(value) ) {
                        System.err.println("Warning: deleted object found!");
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;
                        _fhs[i].setValue( object, null, tx.getClassLoader() );
                        break;
                    }

                    if ( tx.isAutoStore() || _fhs[i].isDependent() )
                        if ( value != tx.fetch( fieldEngine, fieldClassMolder, fields[i], null ) )
                            throw new DuplicateIdentityException("");
                } else {
                    if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                        updatePersist = true;
                    updateCache = true;

                    if ( _fhs[i].isDependent() ) {
                        if ( fields[i] != null ) {
                            Object reldel = tx.fetch( fieldEngine, fieldClassMolder, fields[i], null );
                            if ( reldel != null )
                                tx.delete( reldel );
                        }

                        if ( value != null && !tx.isRecorded( value ) ) {
                            tx.markCreate( fieldEngine, fieldClassMolder, value, oid );
                        }

                    } else if ( tx.isAutoStore() ) {
                        if ( fields[i] != null ) {
                            Object deref = tx.fetch( fieldEngine, fieldClassMolder, fields[i], null );
                            if ( deref != null )
                                fieldClassMolder.removeRelation( tx, deref, this, object );
                        }

                        if ( value != null && !tx.isRecorded( value ) )
                            tx.markCreate( fieldEngine, fieldClassMolder, value, null );
                    } else {
                        if ( fields[i] != null ) {
                            Object deref = tx.fetch( fieldEngine, fieldClassMolder, fields[i], null );
                            if ( deref != null )
                                fieldClassMolder.removeRelation( tx, deref, this, object );
                        }

                        // yip: user're pretty easily to run into cache
                        // integrity problem here, if user forgot to create
                        // "value" explicitly. Let's put error message here
                        if ( value != null && !tx.isRecorded( value ) )
                            throw new PersistenceException(
                            "Object, "+object+", links to another object, "+value
                            +" that is not loaded/updated/created in this transaction");
                    }
                }
                break;

            case FieldMolder.ONE_TO_MANY:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                value = _fhs[i].getValue( object, tx.getClassLoader() );
                orgFields = (ArrayList)fields[i];
                if ( ! (value instanceof Lazy) ) {
                    Collection removed = getRemovedIdsList( tx, orgFields, value, fieldClassMolder );
                    Iterator removedItor = removed.iterator();
                    if ( removedItor.hasNext() ) {
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;
                    }
                    while ( removedItor.hasNext() ) {
                        Object removedId = removedItor.next();
                        Object reldel = tx.fetch( fieldEngine, fieldClassMolder, removedId, null );
                        if ( reldel != null ) {
                            if ( _fhs[i].isDependent() )
                                tx.delete( reldel );
                            else
                                fieldClassMolder.removeRelation( tx, reldel, this, object );
                        } else {
                            // should i notify user that the object does not exist?
                            // user can't delete dependent object himself. So, must
                            // error.
                        }
                    }

                    Collection added = getAddedValuesList( tx, orgFields, value, fieldClassMolder );
                    Iterator addedItor = added.iterator();
                    if ( addedItor.hasNext() ) {
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;
                    }
                    while ( addedItor.hasNext() ) {
                        Object addedValue = addedItor.next();
                        if ( _fhs[i].isDependent() ) {
                            if ( !tx.isRecorded( addedValue ) ) {
                                tx.markCreate( fieldEngine, fieldClassMolder, addedValue, oid );
                            } else {
                                // should i notify user that the object does not exist?
                                // user can't create dependent object himself. So, must be
                                // an error.
                            }
                        } else if ( tx.isAutoStore() ) {
                            if ( !tx.isRecorded( addedValue ) )
                                tx.markCreate( fieldEngine, fieldClassMolder, addedValue, null );
                        }
                    }

                    // it would be good if we also compare the new field element with
                    // the element in the transaction, when debug is set true
                } else {
                    RelationCollection lazy = (RelationCollection) value;

                    // this RelationCollection has to clean up its state at the end of the
                    // transaction
                    tx.addTxSynchronizable(lazy);

                    ArrayList deleted = lazy.getDeleted();
                    if ( !deleted.isEmpty() ) {
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;

                        //if ( _fhs[i].isDependent() ) {
                        itor = deleted.iterator();
                        while ( itor.hasNext() ) {
                            updateCache = true;
                            Object toBeDeleted = lazy.find( itor.next() );
                            if ( toBeDeleted != null && tx.isPersistent( toBeDeleted ) ) {
                                if ( _fhs[i].isDependent() )
                                    tx.delete( toBeDeleted );
                                else
                                    fieldClassMolder.removeRelation( tx, toBeDeleted, this, object );
                            } else {
                                // what to do if it happens?
                            }
                        }
                    }

                    ArrayList added = lazy.getAdded();
                    if ( !added.isEmpty() ) {
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;

                        if ( _fhs[i].isDependent() ) {
                            itor = added.iterator();
                            while ( itor.hasNext() ) {
                                Object toBeAdded = lazy.find( itor.next() );
                                if ( toBeAdded != null ) {
                                    tx.markCreate( fieldEngine, fieldClassMolder, toBeAdded, oid );
                                } else {
                                    // what to do if it happens?
                                }
                            }
                        } else if ( tx.isAutoStore() ) {
                            itor = added.iterator();
                            while ( itor.hasNext() ) {
                                Object toBeAdded = lazy.find( itor.next() );
                                if ( toBeAdded != null )
                                    if ( !tx.isRecorded( toBeAdded ) )
                                        tx.markCreate( fieldEngine, fieldClassMolder, toBeAdded, null );
                            }
                        }
                    }

                }
                break;
            case FieldMolder.MANY_TO_MANY:

                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                value = _fhs[i].getValue( object, tx.getClassLoader() );
                orgFields = (ArrayList) fields[i];
                if ( ! (value instanceof Lazy) ) {
                    Collection removed = getRemovedIdsList( tx, orgFields, value, fieldClassMolder );
                    Iterator removedItor = removed.iterator();
                    if ( removedItor.hasNext() ) {
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;
                    }
                    while ( removedItor.hasNext() ) {
                        Object id = removedItor.next();
                        // must be loaded thur transaction, so that the related object
                        // is properly locked and updated before we delete it.
                        if ( !tx.isDeletedByOID( new OID( fieldEngine, fieldClassMolder, id ) ) ) {
                            Object reldel = tx.load( fieldEngine, fieldClassMolder, id, null, null );
                            if ( reldel != null && tx.isPersistent( reldel ) ) {
                                tx.writeLock( reldel, tx.getLockTimeout() );

                                _fhs[i].getRelationLoader().deleteRelation(
                                (Connection)tx.getConnection(oid.getLockEngine()),
                                oid.getIdentity(), id );

                                fieldClassMolder.removeRelation( tx, reldel, this, object );
                            }
                        }
                    }

                    Collection added = getAddedValuesList( tx, orgFields, value, fieldClassMolder );
                    Iterator addedItor = added.iterator();
                    if ( addedItor.hasNext() ) {
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;
                    }
                    while ( addedItor.hasNext() ) {
                        Object addedField = addedItor.next();
                        tx.markModified( addedField, false/*updatePersist*/, true/*updateCache*/ );

                        if ( tx.isPersistent( addedField ) ) {
                            _fhs[i].getRelationLoader().createRelation(
                            (Connection)tx.getConnection(oid.getLockEngine()),
                            oid.getIdentity(), fieldClassMolder.getIdentity( tx, addedField ) );
                        } else {
                            if ( tx.isAutoStore() )
                                if ( !tx.isDeleted( addedField ) )
                                    tx.markCreate( fieldEngine, fieldClassMolder, addedField, null );
                        }
                    }

                } else {
                    RelationCollection lazy = (RelationCollection) value;

                    // this RelationCollection has to clean up its state at the end of the
                    // transaction
                    tx.addTxSynchronizable(lazy);

                    ArrayList deleted = lazy.getDeleted();
                    if ( !deleted.isEmpty() ) {
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;

                        itor = deleted.iterator();
                        while ( itor.hasNext() ) {
                            updateCache = true;
                            Object deletedId = itor.next();
                            Object toBeDeleted = lazy.find( deletedId );
                            if ( toBeDeleted != null ) {
                                if ( tx.isPersistent(toBeDeleted) ) {
                                    tx.writeLock( toBeDeleted, 0 );

                                    _fhs[i].getRelationLoader().deleteRelation(
                                    (Connection)tx.getConnection(oid.getLockEngine()),
                                    oid.getIdentity(), deletedId );

                                    fieldClassMolder.removeRelation( tx, toBeDeleted, this, object );
                                }
                            } else {
                                // what to do if it happens?
                            }
                        }
                    }

                    ArrayList added = lazy.getAdded();
                    if ( !added.isEmpty() ) {
                        if ( _fhs[i].isStored() && _fhs[i].isCheckDirty() )
                            updatePersist = true;
                        updateCache = true;

                        itor = added.iterator();
                        while ( itor.hasNext() ) {
                            Object addedId = itor.next();
                            Object toBeAdded = lazy.find( addedId );
                            if ( toBeAdded != null ) {
                                if ( tx.isPersistent( toBeAdded ) ) {
                                    _fhs[i].getRelationLoader().createRelation(
                                    (Connection)tx.getConnection(oid.getLockEngine()),
                                    oid.getIdentity(), addedId );
                                } else {
                                    if ( tx.isAutoStore() )
                                        if ( !tx.isRecorded( toBeAdded ) )
                                            tx.markCreate( fieldEngine, fieldClassMolder, toBeAdded, null );
                                }
                            } else {
                                // what to do if it happens?
                            }
                        }
                    }
                }
                break;
            default:
            }
        }


        tx.markModified( object, updatePersist, updateCache );

        if ( updateCache || updatePersist )
            tx.writeLock( object, timeout );

        return updateCache;
    }

    /**
     * Store a data object into the persistent storage of the base class of this
     * ClassMolder.
     *
     * @param tx Transaction in action
     * @param oid the object identity of the stored object
     * @param locker the dirty check cache of the object
     * @param object the object to be stored
     */
    public void store( TransactionContext tx, OID oid, DepositBox locker, Object object )
            throws DuplicateIdentityException, PersistenceException,
            ObjectModifiedException, ObjectDeletedException {

        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        Object[] newfields;
        Object[] fields;
        Object value;
        int fieldType;

        if ( oid.getIdentity() == null )
            throw new PersistenceException("The identities of the object to be stored is null");

        if ( !oid.getIdentity().equals( getIdentity( tx, object ) ) )
            throw new PersistenceException("Identities changes is not allowed!");

        fields = (Object[]) locker.getObject( tx );

        if ( fields == null )
            throw new PersistenceException("Object, "+oid+",  isn't loaded in the persistence storage!");

        newfields = new Object[_fhs.length];
        for ( int i=0; i<newfields.length; i++ ) {
            fieldType = _fhs[i].getFieldType();
            switch (fieldType) {
            case FieldMolder.PRIMITIVE:
                newfields[i] = _fhs[i].getValue( object, tx.getClassLoader() );
                break;
            case FieldMolder.SERIALIZABLE:
                try {
                    Object dependent = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( dependent != null ) {
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        ObjectOutputStream os = new ObjectOutputStream( bos );
                        os.writeObject( dependent );
                        newfields[i] = bos.toByteArray();
                    } else {
                        newfields[i] = null;
                    }
                } catch ( IOException e ) {
                    throw new PersistenceException( "Error during serializing dependent object", e );
                }
                break;

            case FieldMolder.PERSISTANCECAPABLE:
                if ( _fhs[i].isStored() ) {
                    fieldClassMolder = _fhs[i].getFieldClassMolder();
                    fieldEngine = _fhs[i].getFieldLockEngine();
                    value = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( value != null )
                        newfields[i] = fieldClassMolder.getIdentity( tx, value );
                }
                break;
            case FieldMolder.ONE_TO_MANY:
                break;
            case FieldMolder.MANY_TO_MANY:
                break;
            default:
            }
        }
        Object stamp = _persistence.store( tx.getConnection(oid.getLockEngine()),
                newfields, oid.getIdentity(), fields, oid.getStamp() );
        oid.setStamp( stamp );
    }

    /**
     * It is assumed the returned collection will not be modified. Any modification
     * to the returned collection may or may not affect the original collection or map.
     */
    private Collection getAddedValuesList( TransactionContext tx, ArrayList orgIds, Object collection, ClassMolder ch ) {

        if ( collection == null )
            return new ArrayList(0);

        if ( collection instanceof Map ) {
            if ( orgIds == null || orgIds.size() == 0 ) {
                if ( collection == null )
                    return new ArrayList(0);
                else
                    return ((Map) collection).values();
            }

            Map newMap = (Map) collection;
            ArrayList added = new ArrayList( newMap.size() );
            Iterator newItor = newMap.entrySet().iterator();
            while ( newItor.hasNext() ) {
                Map.Entry newId = (Map.Entry) newItor.next();
                if ( !orgIds.contains( newId.getKey() ) )
                    added.add( newId.getValue() );
            }
            return added;
        }

        if ( collection instanceof Collection ) {
            if ( orgIds == null || orgIds.size() == 0 ) {
                if ( collection == null )
                    return new ArrayList(0);
                else
                    return (Collection) collection;
            }

            if ( collection == null )
                return new ArrayList(0);

            Collection newValues = (Collection) collection;
            ArrayList added = new ArrayList( newValues.size() );
            Iterator newItor = newValues.iterator();
            while ( newItor.hasNext() ) {
                Object newValue = newItor.next();
                Object newId = ch.getIdentity( tx, newValue );
                if ( newId == null || !orgIds.contains( newId ) )
                    added.add( newValue );
            }
            return added;
        }

        if ( collection.getClass().isArray() ) {
            if ( orgIds == null || orgIds.size() == 0 ) {
                if ( collection == null )
                    return new ArrayList(0);
                else {
                  Object[] newValues = (Object[]) collection;
                  ArrayList result = new ArrayList(newValues.length);
                  for(int i=0;i<newValues.length;i++) {
                    result.add(newValues[i]);
                  }
                  return result;
                }
            }

            if ( collection == null )
                return new ArrayList(0);

            Object[] newValues = (Object[]) collection;
            ArrayList added = new ArrayList( newValues.length );
            for(int i=0;i<newValues.length;i++) {
                Object newValue = newValues[i];
                Object newId = ch.getIdentity( tx, newValue );
                if ( newId == null || !orgIds.contains( newId ) )
                    added.add( newValue );
            }
            return added;
        }

        throw new IllegalArgumentException( "Collection type "+collection.getClass().getName()+" is not supported!" );
    }

    /**
     * It is assumed the returned collection will not be modified. Any modification
     * to the returned collection may or may not affect the original collection or map.
     */
    private Collection getRemovedIdsList( TransactionContext tx, ArrayList orgIds, Object collection, ClassMolder ch ) {

        if ( collection == null ) {
            if ( orgIds == null )
                return new ArrayList(0);
            else
                return orgIds;
        }

        if ( collection instanceof Map ) {
            if ( orgIds == null || orgIds.size() == 0 )
                return new ArrayList(0);

            Map newMap = (Map) collection;
            Iterator orgItor = orgIds.iterator();
            ArrayList removed = new ArrayList( orgIds.size() );
            while ( orgItor.hasNext() ) {
                Object id = orgItor.next();
                if ( !newMap.containsKey( id ) )
                    removed.add( id );
            }
            return removed;
        }

        if ( collection instanceof Collection ) {
            if ( orgIds == null || orgIds.size() == 0 )
                return new ArrayList(0);

            Collection newCol = (Collection) collection;
            Iterator orgItor = orgIds.iterator();
            ArrayList removed = new ArrayList(0);

            // make a new map of key and value of the new collection
            HashMap newMap = new HashMap();
            Iterator newColItor = newCol.iterator();
            while ( newColItor.hasNext() ) {
                Object newObject = newColItor.next();
                Object newId = ch.getIdentity( tx, newObject );
                if ( newId != null )
                    newMap.put( newId, newObject );
            }
            while ( orgItor.hasNext() ) {
                Object id = orgItor.next();
                if ( !newMap.containsKey( id ) )
                    removed.add( id );
            }
            return removed;
        }

        if ( collection.getClass().isArray() ) {
            if ( orgIds == null || orgIds.size() == 0 )
                return new ArrayList(0);

            Object[] newCol = (Object[]) collection;
            Iterator orgItor = orgIds.iterator();
            ArrayList removed = new ArrayList(0);

            // make a new map of key and value of the new collection
            HashMap newMap = new HashMap();
            for (int i=0;i<newCol.length;i++) {
                Object newObject = newCol[i];
                Object newId = ch.getIdentity( tx, newObject );
                if ( newId != null )
                    newMap.put( newId, newObject );
            }
            while ( orgItor.hasNext() ) {
                Object id = orgItor.next();
                if ( !newMap.containsKey( id ) )
                    removed.add( id );
            }
            return removed;
        }

        throw new IllegalArgumentException( "Collection type "+collection.getClass().getName()+" is not supported!" );
    }

    /**
     * Update the object which loaded or created in the other transaction to
     * the persistent storage.
     *
     * @param tx Transaction in action
     * @param oid the object identity of the stored object
     * @param locker the dirty check cache of the object
     * @param object the object to be stored
     * @return boolean true if the updating object should be created
     */
    public boolean update( TransactionContext tx, OID oid, DepositBox locker, Object object, AccessMode suggestedAccessMode )
            throws PersistenceException, ObjectModifiedException {

        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        Object[] fields;
        Object ids;
        AccessMode am;
        Object value;
        Object stamp;
        Object[] temp;
        int fieldType;
        Object o;
        AccessMode accessMode = getAccessMode( suggestedAccessMode );

        fields = (Object[]) locker.getObject( tx );

        if ( !isDependent() && !_timeStampable )
            throw new IllegalArgumentException("A master object that involves in a long transaction must be a TimeStampable!");

        long lockTimestamp = locker.getTimeStamp();
        long objectTimestamp = _timeStampable? ((TimeStampable)object).jdoGetTimeStamp(): 1;

        if ( objectTimestamp > 0 && oid.getIdentity() != null ) {
            // valid range of timestamp

            if ( _timeStampable && objectTimestamp != lockTimestamp )
                throw new ObjectModifiedException("Timestamp mismatched!");

            if ( !_timeStampable && isDependent() && fields == null  ) {
                // allow a dependent object not implements timeStampable
                fields = new Object[_fhs.length];
                Connection conn = (Connection)tx.getConnection(oid.getLockEngine());
                stamp = _persistence.load( conn, fields, oid.getIdentity(), accessMode );
                oid.setDbLock( accessMode == AccessMode.DbLocked );
                locker.setObject( tx, fields );
            }

            ids = oid.getIdentity();

            // load the original field into the transaction. so, store will
            // have something to compare later.
            try {
                for ( int i=0; i <_fhs.length; i++ ) {
                    fieldType = _fhs[i].getFieldType();
                    switch (fieldType) {
                    case FieldMolder.PRIMITIVE:
                        break;
                    case FieldMolder.SERIALIZABLE:
                        break;
                    case FieldMolder.PERSISTANCECAPABLE:
                        fieldClassMolder = _fhs[i].getFieldClassMolder();
                        fieldEngine = _fhs[i].getFieldLockEngine();
                        o = _fhs[i].getValue( object, tx.getClassLoader() );
                        if ( _fhs[i].isDependent() ) {
                            // depedent class won't have persistenceInfo in LockEngine
                            // must look at fieldMolder for it

                            if ( o != null && !tx.isRecorded(o) )
                                tx.markUpdate( fieldEngine, fieldClassMolder, o, oid );

                            // load the cached dependent object from the data store.
                            // The loaded will be compared with the new one
                            if ( fields[i] != null )
                                tx.load( fieldEngine, fieldClassMolder, fields[i], null, suggestedAccessMode );
                        } else if ( tx.isAutoStore() ) {
                            if ( o != null && !tx.isRecorded(o) )
                                tx.markUpdate( fieldEngine, fieldClassMolder, o, null );

                            if ( fields[i] != null )
                                tx.load( fieldEngine, fieldClassMolder, fields[i], null, suggestedAccessMode );
                        }
                        break;

                    case FieldMolder.ONE_TO_MANY:
                        fieldClassMolder = _fhs[i].getFieldClassMolder();
                        fieldEngine = _fhs[i].getFieldLockEngine();
                        if ( _fhs[i].isDependent() ) {
                            if ( !_fhs[i].isLazy() ) {
                                Iterator itor = getIterator( _fhs[i].getValue( object, tx.getClassLoader() ) );
                                ArrayList v = (ArrayList)fields[i];
                                ArrayList newSetOfIds = new ArrayList();

                                // iterate the collection of this data object field
                                while ( itor.hasNext() ) {
                                    Object element = itor.next();
                                    Object actualIdentity = fieldClassMolder.getActualIdentity( tx, element );
                                    newSetOfIds.add( actualIdentity );
                                    if ( v != null && v.contains( actualIdentity ) ) {
                                        if ( !tx.isRecorded( element ) )
                                            tx.markUpdate( fieldEngine, fieldClassMolder, element, oid );
                                    } else {
                                        /*
                                        if ( !tx.isRecorded( element ) )
                                            tx.markCreate( fieldEngine, fieldClassMolder, element, oid );
                                         */
                                    }
                                }
                                if ( v != null ) {
                                    for ( int j=0,l=v.size(); j<l; j++ ) {
                                        if ( !newSetOfIds.contains( v.get(j) ) ) {
                                            // load all the dependent object in cache for modification
                                            // check at commit time.
                                            tx.load( oid.getLockEngine(), fieldClassMolder, v.get(j), null, suggestedAccessMode );

                                        }
                                    }
                                }
                            } else {
                                ArrayList avlist = (ArrayList) fields[i];
                                fieldClassMolder = _fhs[i].getFieldClassMolder();
                                fieldEngine = _fhs[i].getFieldLockEngine();
                                RelationCollection relcol = new RelationCollection( tx, oid, fieldEngine, fieldClassMolder, accessMode, avlist );
                            }
                        } else if ( tx.isAutoStore() ) {
                            Iterator itor = getIterator( _fhs[i].getValue( object, tx.getClassLoader() ) );
                            ArrayList v = (ArrayList)fields[i];
                            ArrayList newSetOfIds = new ArrayList();

                            // iterate the collection of this data object field
                            while ( itor.hasNext() ) {
                                Object element = itor.next();
                                Object actualIdentity = fieldClassMolder.getActualIdentity( tx, element );
                                newSetOfIds.add( actualIdentity );
                                if ( v != null && v.contains( actualIdentity ) ) {
                                    if ( !tx.isRecorded( element ) )
                                        tx.markUpdate( fieldEngine, fieldClassMolder, element, null );
                                } else {
                                    if ( !tx.isRecorded( element ) )
                                        tx.markUpdate( fieldEngine, fieldClassMolder, element, null );
                                }
                            }
                            // load all old objects for comparison in the preStore state
                            if ( v != null ) {
                                for ( int j=0,l=v.size(); j<l; j++ ) {
                                    if ( !newSetOfIds.contains( v.get(j) ) ) {
                                        // load all the dependent object in cache for modification
                                        // check at commit time.
                                        tx.load( oid.getLockEngine(), fieldClassMolder, v.get(j), null, suggestedAccessMode );
                                    }
                                }
                            }
                        }
                        break;
                    case FieldMolder.MANY_TO_MANY:
                        fieldClassMolder = _fhs[i].getFieldClassMolder();
                        fieldEngine = _fhs[i].getFieldLockEngine();
                        if ( tx.isAutoStore() ) {
                            Iterator itor = getIterator( _fhs[i].getValue( object, tx.getClassLoader() ) );
                            ArrayList v = (ArrayList)fields[i];
                            ArrayList newSetOfIds = new ArrayList();

                            // iterate the collection of this data object field
                            while ( itor.hasNext() ) {
                                Object element = itor.next();
                                Object actualIdentity = fieldClassMolder.getActualIdentity( tx, element );
                                newSetOfIds.add( actualIdentity );
                                if ( v != null && v.contains( actualIdentity ) ) {
                                    if ( !tx.isRecorded( element ) )
                                        tx.markUpdate( fieldEngine, fieldClassMolder, element, null );
                                } else {
                                    if ( !tx.isRecorded( element ) )
                                        tx.markUpdate( fieldEngine, fieldClassMolder, element, null );
                                }
                            }
                            // load all old objects for comparison in the preStore state
                            if ( v != null ) {
                                for ( int j=0,l=v.size(); j<l; j++ ) {
                                    if ( !newSetOfIds.contains( v.get(j) ) ) {
                                        // load all the dependent object in cache for modification
                                        // check at commit time.
                                        tx.load( oid.getLockEngine(), fieldClassMolder, v.get(j), null, suggestedAccessMode );
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
            } catch ( ObjectNotFoundException e ) {
                e.printStackTrace();
                throw new ObjectModifiedException("dependent object deleted concurrently");
            }
            return false;
        } else if ( objectTimestamp == TimeStampable.NO_TIMESTAMP || objectTimestamp == 1 ) {
            // work almost like create, except update the sub field instead of create
            // iterate all the fields and mark all the dependent object.
            boolean updateCache = false;

            for ( int i=0; i<_fhs.length; i++ ) {
                fieldType = _fhs[i].getFieldType();
                switch (fieldType) {
                case FieldMolder.PRIMITIVE:
                case FieldMolder.SERIALIZABLE:
                    // nothing need to be done here for primitive
                    break;

                case FieldMolder.PERSISTANCECAPABLE:
                    // create dependent object if exists
                    fieldClassMolder = _fhs[i].getFieldClassMolder();
                    fieldEngine = _fhs[i].getFieldLockEngine();
                    o = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( o != null ) {
                        if ( _fhs[i].isDependent() ) {
                            // creation of dependent object should be delayed to the
                            // preStore state.
                            // otherwise, in the case of keygenerator being used in both
                            // master and dependent object, and if an dependent
                            // object is replaced by another before commit, the
                            // orginial dependent object will not be removed.
                            //
                            // the only disadvantage for that appoarch is that an
                            // OQL Query will not able to include the newly generated
                            // dependent object.
                            if ( !tx.isRecorded( o ) ) {
                                tx.markCreate( fieldEngine, fieldClassMolder, o, oid );
                                if ( !_fhs[i].isStored() && fieldClassMolder._isKeyGenUsed ) {
                                    updateCache = true;
                                }
                            } else {}
                                // fail-fast principle: if the object depend on another object,
                                // throw exception
                                // if ( !tx.isDepended( oid, o ) )
                                //    throw new PersistenceException("Dependent object may not change its master. Object: "+o+" new master: "+oid);
                        } else if ( tx.isAutoStore() ) {
                            if ( !tx.isRecorded( o ) ) {
                                // related object should be created right the way, if autoStore
                                // is enabled, to obtain a database lock on the row. If both side
                                // uses keygenerator, the current object will be updated in the
                                // store state.
                                boolean creating = tx.markUpdate( fieldEngine, fieldClassMolder, o, null );
                                // if _fhs[i].isStore is true for this field,
                                // and if key generator is used
                                // and if the related object is replaced this object by null
                                // and if everything else is not modified
                                // then, objectModifiedException will be thrown
                                // there are two solutions, first introduce preCreate state,
                                // and walk the create graph, and create non-store object
                                // first. However, it doesn't guarantee solution. because
                                // every object may have field which uses key-generator
                                // second, we can do another SQLStatement at the very end of
                                // this method.
                                // note, one-many and many-many doesn't affected, because
                                // it is always non-store fields.
                                if ( creating && !_fhs[i].isStored() && fieldClassMolder._isKeyGenUsed ) {
                                    updateCache = true;
                                }
                            }
                        }
                    }
                    break;

                case FieldMolder.ONE_TO_MANY:
                    // create dependent objects if exists
                    fieldClassMolder = _fhs[i].getFieldClassMolder();
                    fieldEngine = _fhs[i].getFieldLockEngine();
                    o = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( o != null ) {
                        Iterator itor = getIterator( o );
                        while (itor.hasNext()) {
                            Object oo = itor.next();
                            if ( _fhs[i].isDependent() ) {
                                if ( !tx.isRecorded( oo ) ) {
                                    //autoCreated = true;
                                    tx.markCreate( fieldEngine, fieldClassMolder, oo, oid );
                                    if ( fieldClassMolder._isKeyGenUsed )
                                        updateCache = true;
                                } else
                                    // fail-fast principle: if the object depend on another object,
                                    // throw exception
                                    if ( !tx.isDepended( oid, oo ) )
                                        throw new PersistenceException("Dependent object may not change its master");
                            } else if ( tx.isAutoStore() ) {
                                if ( !tx.isRecorded( oo ) ) {
                                    boolean creating = tx.markUpdate( fieldEngine, fieldClassMolder, oo, null );
                                    if ( creating && fieldClassMolder._isKeyGenUsed )
                                        updateCache = true;
                                }
                            }
                        }
                    }
                    break;

                case FieldMolder.MANY_TO_MANY:
                    // create relation if the relation table
                    fieldClassMolder = _fhs[i].getFieldClassMolder();
                    fieldEngine = _fhs[i].getFieldLockEngine();
                    o = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( o != null ) {
                        Iterator itor = getIterator( o );
                        // many-to-many relation is never dependent relation
                        while (itor.hasNext()) {
                            Object oo = itor.next();
                            if ( tx.isAutoStore() && !tx.isRecorded( oo ) ) {
                                boolean creating = tx.markUpdate( fieldEngine, fieldClassMolder, oo, null );
                                if ( creating )
                                    updateCache = true;
                            }
                        }
                    }
                    break;
                }
            }

            tx.markModified( object, false, updateCache );
            return true;
        } else {
            System.err.println( "object: "+object+" timestamp: "+objectTimestamp+"lockertimestamp: "+lockTimestamp );
            throw new ObjectModifiedException("Invalid object timestamp detected.");
        }
    }

    /**
     * Update the dirty checking cache. This method is called after a transaction completed successfully.
     *
     * @param tx - transaction in action
     * @param oid - object's identity of the target object
     * @param locker - the dirty checking cache of the target object
     * @param object - the target object
     */
    public void updateCache( TransactionContext tx, OID oid, DepositBox locker, Object object ) {

        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        ArrayList fids;
        Object fid;
        Object[] fields;
        Object value;

        int fieldType;

        fields = new Object[_fhs.length];

        if ( oid.getIdentity() == null )
            throw new IllegalStateException("The identities of the cache to be updated is null");

        for ( int i=0; i < _fhs.length; i++ ) {
            fieldType = _fhs[i].getFieldType();
            switch (fieldType) {
            case FieldMolder.PRIMITIVE:
                // should give some attemp to reduce new object array
                fields[i] = _fhs[i].getValue( object, tx.getClassLoader() );
                break;

            case FieldMolder.SERIALIZABLE:
                try {
                    Object o = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( o != null ) {
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        ObjectOutputStream os = new ObjectOutputStream( bos );
                        os.writeObject( o );
                        fields[i] = bos.toByteArray();
                    } else {
                        fields[i] = null;
                    }
                } catch ( IOException e ) {
                    // It won't happen. ByteArrayOutputStream will not throw IOException
                }
                break;
            case FieldMolder.PERSISTANCECAPABLE:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                value = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( value != null ) {
                    fid = fieldClassMolder.getIdentity( tx, value );
                    if ( fid != null ) {
                        fields[i] = fid;
                    }
                } else {
                    fields[i] = null;
                }
                break;
            case FieldMolder.ONE_TO_MANY:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                value = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( value != null ) {
                    if ( !(value instanceof Lazy) ) {
                        fids = extractIdentityList( tx, fieldClassMolder, value );
                        fields[i] = fids;
                    } else {
                        RelationCollection lazy = (RelationCollection) value;
                        fids = (ArrayList)lazy.getIdentitiesList().clone();
                        fields[i] = fids;
                    }
                } else {
                    fields[i] = null;
                }
                break;
            case FieldMolder.MANY_TO_MANY:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                value = _fhs[i].getValue( object, tx.getClassLoader() );
                if ( value != null ) {
                    if ( !(value instanceof Lazy ) ) {
                        fids = extractIdentityList( tx, fieldClassMolder, value );
                        fields[i] = fids;
                    } else {
                        RelationCollection lazy = (RelationCollection) value;
                        fids = (ArrayList)lazy.getIdentitiesList().clone();
                        fields[i] = fids;
                    }
                } else {
                    fields[i] = null;
                }
                break;
            default:
                throw new IllegalArgumentException("Field type invalid!");
            }
        }

        locker.setObject( tx, fields );
        if ( object instanceof TimeStampable ) {
            ((TimeStampable)object).jdoSetTimeStamp( locker.getTimeStamp() );
        }

    }

    /**
     * It method is called by delete to delete the extended object of the base
     * type from the Persistence.
     *
     */
    private static void deleteExtend( TransactionContext tx, ClassMolder extend, Object identity )
            throws ObjectNotFoundException, PersistenceException {
        // if the extend field contains dependent of field type,
        // fields must be loaded, so that we get the foreign key
        // of the depedent table. We may get no result. And, it
        // is good and we don't have to process further.
        // it will be cheaper if we go directly to SQL and load
        // only the foreign field needed. But, it require extra
        // method in Persistent SPI. So, maybe we rather stay
        // with the expensive appoarch.

        // if there is any depedent field of reference type, the simplest way is
        // to load the dependent objects and delete it using tansaction.
        // (Will ObjectNotFound throws in the loading??)

        // if the extend field has many-to-many field, we must delete the
        // relation from he relation table.
        Object[] persistFields = null;
        for ( int i=0; i < extend._fhs.length; i++ ) {
            if ( extend._fhs[i].isDependent() ) {
                try {
                    if ( persistFields == null ) {

                        persistFields = new Object[extend._fhs.length];
                        extend._persistence.load( (Connection)tx.getConnection(extend._engine),
                        persistFields, identity, AccessMode.ReadOnly );
                    }

                    if ( extend._fhs[i].isMulti() ) {
                        ArrayList listOfIds = (ArrayList)persistFields[i];
                        for ( int j=0; i < listOfIds.size(); j++ ) {
                            extend._persistence.delete( tx.getConnection(extend._fhs[i].getFieldLockEngine()),
                            listOfIds.get(j) );
                        }
                    }
                } catch ( ObjectNotFoundException e ) {
                }
            } else if ( extend._fhs[i].isManyToMany() ) {
                extend._fhs[i].getRelationLoader().deleteRelation(
                (Connection)tx.getConnection(extend._fhs[i].getFieldLockEngine()),
                identity );
            }
        }
    }

    /**
     * Delete an object of the base type from the persistence storage.
     * All object to be deleted by this method will be <tt>markDelete</tt>
     * before it method is called.
     *
     * @param tx - transaction in action
     * @param oid - the object identity of the target object
     */
    public void delete( TransactionContext tx, OID oid )
            throws PersistenceException {

        Object ids = oid.getIdentity();

        for( int i=0; i < _fhs.length; i++ ) {
            if( _fhs[i].isManyToMany() ) {
                _fhs[i].getRelationLoader().deleteRelation(
                  (Connection)tx.getConnection(oid.getLockEngine()),
                  ids);
            }
        }

        _persistence.delete( (Connection)tx.getConnection(oid.getLockEngine()), ids );

        // All field along the extend path will be deleted by transaction
        // However, everything off the path must be deleted by ClassMolder.

        Vector extendPath = new Vector();
        ClassMolder base = this;
        while ( base != null ) {
            extendPath.add( base );
            base = base._extends;
        }

        base = _depends;
        while ( base != null ) {
            if ( base._extendent != null )
                for ( int i=0; i < base._extendent.size(); i++ )
                    if ( !extendPath.contains( base._extendent.get(i) ) ) {
                        //deleteExtend( tx, (ClassMolder)base._extendent.get(i), ids );
                    } else {
                    }

            base = base._extends;
        }

        if ( _extendent != null ) {
            for ( int i=0; i < _extendent.size(); i++ ) {
                if ( !extendPath.contains( _extendent.get(i) ) ) {
                    //deleteExtend( tx, (ClassMolder)_extendent.get(i), ids );
                }
            }
        }

    }

    /**
     * Prepare to delete an object with the specified identity. If any sub-object
     * should be deleted along with the target object, it should be deleted
     * by this method.
     *
     * @param tx - transaction in action
     * @param oid - object's identity of the target object
     * @param locker - the dirty checking cache of the target object
     * @param object - the target object
     */
    public void markDelete( TransactionContext tx, OID oid, DepositBox locker, Object object )
            throws ObjectNotFoundException, PersistenceException {

        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        Object[] fields;

        fields = (Object[])locker.getObject( tx );


        for ( int i=0; i < _fhs.length; i++ ) {
            int fieldType = _fhs[i].getFieldType();
            switch (fieldType) {
            case FieldMolder.PRIMITIVE:
            case FieldMolder.SERIALIZABLE:
                break;
            case FieldMolder.PERSISTANCECAPABLE:
                // persistanceCapable include many_to_one
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                if ( _fhs[i].isDependent() ) {
                    Object fid = fields[i];
                    Object fetched = null;
                    if ( fid != null ) {
                        fetched = tx.fetch( fieldEngine, fieldClassMolder, fid, null );
                        if ( fetched != null )
                            tx.delete( fetched );
                    }

                    Object fobject = _fhs[i].getValue( object, tx.getClassLoader() );
                    if ( fobject != null && tx.isPersistent( fobject ) ) {
                        tx.delete( fobject );
                    }
                } else {
                    // delete the object from the other side of the relation
                    Object fid = fields[i];
                    Object fetched = null;
                    if ( fid != null ) {
                        fetched = tx.fetch( fieldEngine, fieldClassMolder, fields[i], null );
                        if ( fetched != null )
                            fieldClassMolder.removeRelation( tx, fetched, this, object );
                    }
                }
                break;
            case FieldMolder.ONE_TO_MANY:
                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                // markDelete mix with prestore
                // so, store is not yet called, and only the loaded (or created)
                // relation have to be deleted.
                // not really. cus, the other created relation, may already
                // has reference to this object. so, how to deal with that?
                if ( _fhs[i].isDependent() ) {
                    ArrayList alist = (ArrayList) fields[i];
                    if ( fields[i] != null ) {
                        for ( int j=0; j<alist.size(); j++ ) {
                            Object fid =  alist.get(j);
                            Object fetched = null;
                            if ( fid != null ) {
                                fetched = tx.fetch( fieldEngine, fieldClassMolder, fid, null );
                                if ( fetched != null )
                                    tx.delete( fetched );
                            }
                        }

                        Iterator itor = getIterator( _fhs[i].getValue( object, tx.getClassLoader() ) );
                        while ( itor.hasNext() ) {
                            Object fobject = itor.next();
                            if ( fobject != null && tx.isPersistent( fobject ) ) {
                                tx.delete( fobject );
                            }
                        }
                    }
                } else {
                    if ( fields[i] != null ) {
                        ArrayList alist = (ArrayList) fields[i];
                        for ( int j=0; j<alist.size(); j++ ) {
                            Object fid = alist.get(j);
                            Object fetched = null;
                            if ( fid != null ) {
                                fetched = tx.fetch( fieldEngine, fieldClassMolder, fid, null );
                                if ( fetched != null ) {
                                    fieldClassMolder.removeRelation( tx, fetched, this, object );
                                }
                            }
                        }

                        Iterator itor = getIterator( _fhs[i].getValue( object, tx.getClassLoader() ) );
                        while ( itor.hasNext() ) {
                            Object fobject = itor.next();
                            if ( fobject != null && tx.isPersistent( fobject ) ) {
                                fieldClassMolder.removeRelation( tx, fobject, this, object );
                            }
                        }
                    }
                }
                break;
            case FieldMolder.MANY_TO_MANY:
                // delete the relation in relation table too
                /*
                _fhs[i].getRelationLoader().deleteRelation(
                (Connection)tx.getConnection(oid.getLockEngine()),
                oid.getIdentity() );
                */

                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();
                // markDelete mix with prestore
                // so, store is not yet called, and only the loaded (or created)
                // relation have to be deleted.
                // not really. cus, the other created relation, may already
                // has reference to this object. so, how to deal with that?
                if ( fields[i] != null ) {
                    ArrayList alist = (ArrayList) fields[i];
                    for ( int j=0; j<alist.size(); j++ ) {
                        Object fid = alist.get(j);
                        Object fetched = null;
                        if ( fid != null ) {
                            fetched = tx.fetch( fieldEngine, fieldClassMolder, fid, null );
                            if ( fetched != null ) {
                                fieldClassMolder.removeRelation( tx, fetched, this, object );
                            }
                        }
                    }
                }

                Iterator itor = getIterator( _fhs[i].getValue( object, tx.getClassLoader() ) );
                while ( itor.hasNext() ) {
                    Object fobject = itor.next();
                    if ( fobject != null && tx.isPersistent( fobject ) ) {
                        fieldClassMolder.removeRelation( tx, fobject, this, object );
                    }
                }
                break;
            default:
                throw new PersistenceException("Invalid field type!");
            }
        }
    }

    /**
     * Revert the object back to the state of begining of the transaction
     * If the object is loaded, it will be revert as it was loaded. If the
     * object is created, it will be revert as it was just created.
     *
     * @param tx - transaction in action
     * @param oid - the object identity of the target object
     * @param locker - the dirty checking cache of the target object
     * @param object - the target object
     */
    public void revertObject( TransactionContext tx, OID oid, DepositBox locker, Object object )
            throws PersistenceException {

        ClassMolder fieldClassMolder;
        LockEngine fieldEngine;
        Object[] fields;
        Object value;
        int fieldType;

        if ( oid.getIdentity() == null )
            throw new PersistenceException("The identities of the object to be revert is null");

        fields = (Object[]) locker.getObject( tx );

        setIdentity( tx, object, oid.getIdentity() );

        for ( int i=0; i < _fhs.length; i++ ) {
            fieldType = _fhs[i].getFieldType();
            switch (fieldType) {
            case FieldMolder.PRIMITIVE:
                Object temp = fields[i];
                _fhs[i].setValue( object, temp, tx.getClassLoader() );
                break;
            case FieldMolder.SERIALIZABLE:
                // deserialize byte[] into java object
                try {
                    byte[] bytes = (byte[]) fields[i];
                    if ( bytes != null ) {
                        // The following code can be updated, after Blob-->InputStream
                        // to enhance performance.
                        ByteArrayInputStream bis = new ByteArrayInputStream( bytes );
                        ObjectInputStream os = new ObjectInputStream( bis );
                        Object o = os.readObject();
                        _fhs[i].setValue( object, o, tx.getClassLoader() );
                    } else {
                        _fhs[i].setValue( object, null, tx.getClassLoader() );
                    }
                } catch ( OptionalDataException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                } catch ( ClassNotFoundException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                } catch ( IOException e ) {
                    throw new PersistenceException( "Error while deserializing an dependent object", e );
                }
                break;
            case FieldMolder.PERSISTANCECAPABLE:

                fieldClassMolder = _fhs[i].getFieldClassMolder();
                fieldEngine = _fhs[i].getFieldLockEngine();

                if ( fields[i] != null ) {
                    value = tx.fetch( fieldEngine, fieldClassMolder, fields[i], null );
                    _fhs[i].setValue( object, value, tx.getClassLoader() );
                } else {
                    _fhs[i].setValue( object, null, tx.getClassLoader() );
                }
                break;
            case FieldMolder.ONE_TO_MANY:
            case FieldMolder.MANY_TO_MANY:

                Object o = fields[i];
                if ( o == null ) {
                    _fhs[i].setValue( object, null, tx.getClassLoader() );
                } else if ( !_fhs[i].isLazy() ) { // <-- fix for bug #1046
                    fieldClassMolder = _fhs[i].getFieldClassMolder();
                    fieldEngine = _fhs[i].getFieldLockEngine();

                    Class collectionType = _fhs[i].getCollectionType();

                    ArrayList v = (ArrayList)fields[i];
                    if ( v != null ) {
                      if (collectionType.isArray()) {
                        Object[] arrayValue = (Object[])java.lang.reflect.Array.newInstance(
                                collectionType.getComponentType(), v.size());
                        for ( int j=0,l=v.size(); j<l; j++ ) {
                          arrayValue[j] = tx.fetch( oid.getLockEngine(), fieldClassMolder, v.get(j), null );
                        }
                        _fhs[i].setValue( object, arrayValue, tx.getClassLoader() );
                      } else {
                        CollectionProxy cp = CollectionProxy.create( _fhs[i], object, tx.getClassLoader() );
                        // clear collection
                        _fhs[i].setValue( object, cp.getCollection(), tx.getClassLoader());

                        for ( int j=0,l=v.size(); j<l; j++ ) {
                            cp.add( v.get(j), tx.fetch( oid.getLockEngine(), fieldClassMolder, v.get(j), null ) );
                        }
                        cp.close();
                        //_fhs[i].setValue( object, cp.getCollection() );
                      }
                    } else {
                        _fhs[i].setValue( object, null, tx.getClassLoader() );
                    }
                } else {
                    ArrayList list = (ArrayList) fields[i];
                    fieldClassMolder = _fhs[i].getFieldClassMolder();
                    fieldEngine = _fhs[i].getFieldLockEngine();

                    RelationCollection relcol = new RelationCollection( tx, oid, fieldEngine, fieldClassMolder, null, list );
                    _fhs[i].setValue( object, relcol, tx.getClassLoader() );
                }
                break;
            default:
            }
        }
    }

    /**
     * Acquire a write lock on an object of the base type with the specified
     * identity from the persistence storage.
     *
     * @param tx - Transaction in action
     * @param oid - the object identity of the target object
     * @param locker - the dirty checking cache of the object
     * @param object - the target object
     */
    public void writeLock( TransactionContext tx, OID oid, DepositBox locker, Object object )
            throws PersistenceException {
        // call SQLEngine to lock an record
    }


    /**
     * Return a new instance of the base class with the provided ClassLoader object
     *
     * @param laoder the ClassLoader object to use to create a new object
     * @return Object the object reprenseted by this ClassMolder, and instanciated
     * with the provided ClassLoader instance.
     */
    public Object newInstance( ClassLoader loader ) {
        try {
            if (loader != null )
                return loader.loadClass(_name).newInstance();
            else
                return Class.forName(_name).newInstance();
        } catch (ClassNotFoundException e) {
        } catch ( IllegalAccessException e ) {
        } catch ( InstantiationException e ) {
        } catch ( ExceptionInInitializerError e ) {
        } catch ( SecurityException e ) {
        }
        return null;
    }

    /**
     * Get the effective accessMode of the the base type
     *
     * @param txMode - the default transaction accessMode
     * @return the effective acessMode of the base type
     */
    public AccessMode getAccessMode( AccessMode txMode ) {

        if ( txMode == null )
            return _accessMode;

        if ( _accessMode == AccessMode.ReadOnly || txMode == AccessMode.ReadOnly )
            return AccessMode.ReadOnly;
        if ( _accessMode == AccessMode.DbLocked || txMode == AccessMode.DbLocked )
            return AccessMode.DbLocked;
        if ( _accessMode == AccessMode.Exclusive || txMode == AccessMode.Exclusive )
            return AccessMode.Exclusive;
        return txMode;

    }

    /**
     * Get the callback interceptor of the base type
     *
     */
    public CallbackInterceptor getCallback() {
        return _callback;
    }

    /**
     * Test if the specified identity is the default value of the type.
     */
    public boolean isDefaultIdentity( Object identity ) {

        if ( _ids.length == 1 )
            return _ids[0].isDefault( identity );

        if ( identity == null )
            return true;

        Complex c = (Complex) identity;
        for ( int i = 0; i < c.size(); i++ ) {
            if ( !_ids[i].isDefault( c.get(i) ) )
                return false;
        }
        return true;
    }

    /**
     * Get the identity from a object of the base type
     * If object isn't persistent and key generator is used, returns null
     *
     * @param tx the transaction context
     * @param o - object of the base type
     * @return return an Object[] which contains the identity of the object
     */
    public Object getIdentity( TransactionContext tx, Object o ) {
        // [oleg] In the case where key generator is used,
        // the value of identity is dummy, set it to null
        if ( isKeyGeneratorUsed() && ! (tx.isPersistent(o) || tx.isReadOnly(o))) {
            return null;
        } else {
            return getActualIdentity( tx, o );
        }
    }

    /**
     * Get the identity from a object of the base type
     *
     * @param tx the transaction context
     * @param o - object of the base type
     * @return return an Object[] which contains the identity of the object
     */
    public Object getActualIdentity( TransactionContext tx, Object o ) {
        return getActualIdentity( tx.getClassLoader(), o );
    }

    /**
     * Get the identity from a object of the base type
     *
     * @param loader the current class loader
     * @param o - object of the base type
     * @return return an Object[] which contains the identity of the object
     */
    public Object getActualIdentity( ClassLoader loader, Object o ) {
        Object temp;

        if ( _ids.length == 1 ) {
            return _ids[0].getValue( o, loader );
        } else if ( _ids.length == 2 ) {
            temp = _ids[0].getValue( o, loader );
            return temp==null? null: new Complex( temp, _ids[1].getValue( o, loader ) );
        } else {
        Object[] osIds = new Object[_ids.length];
        for ( int i=0; i<osIds.length; i++ ) {
            osIds[i] = _ids[i].getValue( o, loader );
        }
            if ( osIds[0] == null )
                return null;
            else
                return new Complex( osIds );
        }
    }

    /**
     * Set the identity into an object
     *
     * @param tx the transaction context
     * @param object the object to set the identity
     * @param identity the new identity for the object
     */
    public void setIdentity( TransactionContext tx, Object object, Object identity )
            throws PersistenceException {

        if ( _ids.length > 1 ) {
            if ( identity instanceof Complex ) {
                Complex com = (Complex) identity;
                if ( com.size() != _ids.length )
                    throw new PersistenceException( "Complex size mismatched!" );

                for ( int i=0; i<_ids.length; i++ ) {
                    _ids[i].setValue( object, com.get(i), tx.getClassLoader() );
                }
            }
        } else {
            if ( identity instanceof Complex )
                throw new PersistenceException( "Complex type not accepted!" );
            _ids[0].setValue( object, identity, tx.getClassLoader() );
        }
    }
    /**
     * Get the Persisetence of the base type
     *
     */
    public Persistence getPersistence() {
        return _persistence;
    }

    /**
     * Mutator method to set the PersistenceEngine of
     *
     */
    public void setPersistence( Persistence persist ) {
        _persistence = persist;
    }


    /**
     * Get the base class of this ClassMolder given a ClassLoader
     * @param loader the classloader
     * @return the <code>Class</code> instance
     */
    public Class getJavaClass( ClassLoader loader ) {
        Class result = null;
        try {
            result = ( loader != null) ? loader.loadClass(_name) : Class.forName(_name);
        } catch ( ClassNotFoundException e ) {
            e.printStackTrace();
        }
        return result;
    }

    // ssa, FIXME : is that method necessary ?
    /**
     * check if the current ClassModlder is assignable from the <code>class</code>
     * instance.
     *
     * @param cls the Class to check the assignation
     * @return true if assignable
     */
    public boolean isAssignableFrom ( Class cls ) {
        ClassLoader loader = cls.getClassLoader();
        Class molderClass = null;
        try {
            if ( loader != null )
                molderClass = loader.loadClass( _name );
            else
                molderClass = Class.forName( _name );
        } catch ( ClassNotFoundException e ) {
            return false;
        }
        return molderClass.isAssignableFrom( cls );

    }

    /**
     * Get the fully qualified name of the base type of this ClassMolder
     *
     */
    public String getName() {
        return _name;
    }

    /**
     * Get the FieldMolder of the fields of the base type,
     * except the identity fields.
     */
    public FieldMolder[] getFields() {
        return _fhs;
    }

    /**
     * Get the FieldMolders of the identity fields
     */
    public FieldMolder[] getIds() {
        return _ids;
    }

    /**
     * Get the extends class' ClassMolder
     *
     */
    public ClassMolder getExtends() {
        return _extends;
    }

    /**
     * Get the depends class' ClassMolder
     */
    public ClassMolder getDepends() {
        return _depends;
    }

    /**
     * Get the LockEngine which this ClassMolder belongs to.
     *
     */
    public LockEngine getLockEngine() {
        return _engine;
    }
    /**
     * Return the preferred LRU cache mechanism for caching object of this type
     *
     */
    public int getCacheType() {
        return _cachetype;
    }
    /**
     * Return the preferred LRU cache capacity for caching object of this type
     *
     */
    public int getCacheParam() {
        return _cacheparam;
    }

    /**
     * Return true if the base type of this ClassMolder is an dependent
     * class.
     */
    public boolean isDependent() {
        return _depends != null;
    }

    /**
     * Set all persistence fields of object of the base type to null.
     *
     * @param object - target object
     */
    public void setFieldsNull( Object object ) {
        /*
        for ( int i=0; i < _ids.length; i++ ) {
            _ids[i].setValue( object, null );
        }
        for ( int i=0; i < _fhs.length; i++ ) {
            _fhs[i].setValue( object, null );
        }*/
    }

    /**
     * Mutator method to add a extent ClassMolder
     *
     */
    void addExtendent( ClassMolder ext ) {
        if ( _extendent == null )
            _extendent = new Vector();
        _extendent.add( ext );
    }

    /**
     * Mutator method to add a dependent ClassMolder
     *
     */
    void addDependent( ClassMolder dep ) {
        if ( _dependent == null )
            _dependent = new Vector();
        _dependent.add( dep );
    }
    /**
     * Mutator method to set the extends ClassMolder
     *
     */
    void setExtends( ClassMolder ext ) {
        _extends = ext;
        ext.addExtendent( this );
    }

    /**
     * Mutator method to set the depends ClassMolder
     *
     */
    void setDepends( ClassMolder dep ) {
        _depends = dep;
        dep.addDependent( this );
    }

    /**
     * Return the iterator on values of the specified Collection
     * Or, return the iterator on values of the specified Map
     *
     * @param o - a Collection
     */
    private Iterator getIterator( Object o ) {
        if ( o == null ) {
            return new Iterator() {
                public boolean hasNext() {
                    return false;
                }
                public Object next() {
                    throw new NoSuchElementException();
                }
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        } else if ( o instanceof Collection ) {
            return ((Collection) o).iterator();
        } else if ( o instanceof Map ) {
            return ((Map) o).values().iterator();
          } else if ( o.getClass().isArray() ) {
                  final class ArrayIterator implements java.util.Iterator {
                      Object[] array;
                      int i = 0;
                      ArrayIterator(Object[] array) {
                          this.array = array;
                      }
                     public boolean hasNext() {
                          return i<array.length;
                      }
                      public Object next() {
                          return array[i++];
                      }
                      public void remove() {
                          throw new UnsupportedOperationException();
                      }
                  }
                  return new ArrayIterator((Object[])o);
        } else {
            throw new IllegalArgumentException();
        }
    }



    /**
     * Return all the object identity of a Collection of object of the same
     * type.
     *
     * @param tx the transaction context
     * @param molder - class molder of the type of the objects
     * @param col - a Collection or Vector containing
     * @return an <tt>ArrayList</tt>s which contains list of object identity
     */
    private ArrayList extractIdentityList( TransactionContext tx, ClassMolder molder, Object col ) {
        if ( col == null ) {
            return new ArrayList();
        } else if ( col instanceof Collection ) {
            ArrayList idList = new ArrayList();
            Iterator itor = ((Collection)col).iterator();
            while ( itor.hasNext() ) {
                Object id = molder.getIdentity( tx, itor.next() );
                if ( id != null )
                    idList.add( id );
            }
            return idList;
        } else if ( col instanceof Map ) {
            ArrayList idList = new ArrayList();
            Iterator itor = ((Map)col).keySet().iterator();
            while ( itor.hasNext() ) {
                idList.add( itor.next() );
            }
            return idList;
        } else if ( col.getClass().isArray() ) {
          ArrayList idList = new ArrayList();
          Object[] arrayCol = (Object[])col;
          for (int i=0;i<arrayCol.length;i++) {
            Object id = molder.getIdentity( tx, arrayCol[i] );
              if ( id != null )
                  idList.add( id );
          }
          return idList;
        } else {
            throw new IllegalArgumentException("A Collection or Map is expected!");
        }
    }

    public String toString() {
        return "ClassMolder "+ _name;
    }

    /**
     * Return true if a key generator is used for the base type of this ClassMolder
     */
    public boolean isKeyGeneratorUsed() {
        return _isKeyGenUsed || (_extends != null && _extends. isKeyGeneratorUsed());
    }

}

/**
* This class is a proxy for different types of Colleciton and Maps.
*/
abstract class CollectionProxy {

    abstract Object getCollection();

    abstract void add( Object key, Object value );

    abstract void close();

    static CollectionProxy create( FieldMolder fm, Object object, ClassLoader cl ) {
        Class cls = fm.getCollectionType();
        if ( cls == Vector.class ) {
            return new ColProxy( fm, object, cl, new Vector() );
        } else if ( cls == ArrayList.class ) {
            return new ColProxy( fm, object, cl, new ArrayList() );
        } else if ( cls == Collection.class ) {
            return new ColProxy( fm, object, cl, new ArrayList() );
        } else if ( cls == Set.class ) {
            return new ColProxy( fm, object, cl, new HashSet() );
        } else if ( cls == HashSet.class ) {
            return new ColProxy( fm, object, cl, new HashSet() );
        } else if ( cls == Hashtable.class ) {
            return new MapProxy( fm, object, cl, new Hashtable() );
        } else if ( cls == HashMap.class ) {
            return new MapProxy( fm, object, cl, new HashMap() );
        } else if ( cls == Map.class ) {
            return new MapProxy( fm, object, cl, new HashMap() );
        } else {
          throw new IllegalArgumentException("Collection Proxy doesn't exist for this type : "+cls);
        }
    }

    private static class ColProxy extends CollectionProxy {
        private Collection _col;
        private FieldMolder _fm;
        private Object _object;
        private ClassLoader _cl;
        private ColProxy( FieldMolder fm, Object object, ClassLoader cl, Collection col ) {
            _cl = cl;
            _fm = fm;
            _col = col;
            _object = object;
        }
        Object getCollection() {
            return _col;
        }
        void add( Object key, Object value ) {
            if ( !_fm.isAddable() )
                _col.add( value );
            else
                _fm.addValue( _object, value, _cl );
        }
        void close() {
            if ( !_fm.isAddable() )
                _fm.setValue( _object, _col, _cl );
        }
    }

    private static class MapProxy extends CollectionProxy {
        private Map _map;
        private FieldMolder _fm;
        private Object _object;
        private ClassLoader _cl;
        private MapProxy( FieldMolder fm, Object object, ClassLoader cl, Map map ) {
            _cl = cl;
            _map = map;
            _fm = fm;
            _object = object;
        }
        Object getCollection() {
            return _map;
        }
        void add( Object key, Object value ) {
            _map.put( key, value );
        }
        void close() {
            if ( !_fm.isAddable() )
                _fm.setValue( _object, _map, _cl );
        }

    }
}

TOP

Related Classes of org.exolab.castor.persist.CollectionProxy$MapProxy

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.