Package org.apache.ojb.odmg

Source Code of org.apache.ojb.odmg.ObjectEnvelope$ImageExcetion

package org.apache.ojb.odmg;

/* Copyright 2002-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* @author <a href="mailto:thma@apache.org">Thomas Mahler</a>
* @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
*
*/

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.OJBRuntimeException;
import org.apache.ojb.broker.core.proxy.CollectionProxy;
import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
import org.apache.ojb.broker.core.proxy.IndirectionHandler;
import org.apache.ojb.broker.core.proxy.ProxyHelper;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.CollectionDescriptor;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.metadata.FieldType;
import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
import org.apache.ojb.broker.util.BrokerHelper;
import org.apache.ojb.broker.util.ObjectModification;
import org.apache.ojb.broker.util.ObjectModificationDefaultImpl;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.ojb.odmg.states.ModificationState;
import org.apache.ojb.odmg.states.StateNewDirty;
import org.apache.ojb.odmg.states.StateOldClean;
import org.apache.ojb.odmg.states.StateOldDirty;
import org.apache.ojb.odmg.link.LinkEntry;
import org.apache.ojb.odmg.link.LinkEntryOneToOne;
import org.apache.ojb.odmg.link.LinkEntryOneToN;

/**
* ObjectEnvelope is used during ODMG transactions as a wrapper for a
* persistent objects declaration
*
*/
public class ObjectEnvelope implements ObjectModification
{
    static final long serialVersionUID = -829177767933340522L;
    static final int IS_MATERIALIZED_OBJECT = 11;
    static final int IS_MATERIALIZED_PROXY = 13;
    static final int IS_UNMATERIALIZED_PROXY = 17;
    private Logger log = LoggerFactory.getLogger(ObjectEnvelope.class);

    /**
     * The objects modification state, e.g. Old and Clean
     */
    private ModificationState modificationState = null;
    private Identity oid;
    private Boolean hasChanged;
    private boolean writeLocked;

    /**
     * myObj holds the object we are wrapping.
     */
    private Object myObj;

    /**
     * beforeImage holds a mapping between field
     * names and values at the start of the transaction.
     * afterImage holds the mapping at the
     * end of the transaction.
     */
    private Map beforeImage;
    private Map currentImage;
    private ObjectEnvelopeTable buffer;
    // list of all LinkEntry's
    private List linkEntryList;

    /**
     *
     * Create a wrapper by providing an Object.
     */
    public ObjectEnvelope(ObjectEnvelopeTable buffer, Identity oid, Object obj, boolean isNewObject)
    {
        this.linkEntryList = new ArrayList();
        this.buffer = buffer;
        this.oid = oid;
        // TODO: do we really need to materialize??
        myObj = ProxyHelper.getRealObject(obj);
        /*
        if object is new don't make an image, because we know it needs insert
        */
        if(!isNewObject) refreshObjectImage();
        prepareInitialState(isNewObject);
    }

    public PersistenceBroker getBroker()
    {
        return buffer.getTransaction().getBroker();
    }

    TransactionImpl getTx()
    {
        return buffer.getTransaction();
    }

    ObjectEnvelopeTable getEnvelopeTable()
    {
        return buffer;
    }

    public Map getBeforeImage()
    {
        if(beforeImage == null)
        {
            beforeImage = buildObjectImage(getBroker());
            //prepareToCompare();
        }
        return beforeImage;
    }

    public Map getCurrentImage()
    {
        if(currentImage == null)
        {
            currentImage = buildObjectImage(getBroker());
            //prepareToCompare();
        }
        return currentImage;
    }

    public void close()
    {
        if(currentImage != null)
        {
            Iterator iterator = currentImage.values().iterator();
            while(iterator.hasNext())
            {
                EqualsBase base =  (EqualsBase) iterator.next();
                if(base != null) base.close();
            }
        }
        if(beforeImage != null)
        {
            Iterator iterator = beforeImage.values().iterator();
            while(iterator.hasNext())
            {
                EqualsBase base =  (EqualsBase) iterator.next();
                if(base != null) base.close();
            }
        }
        myObj = null;
    }

    public void refreshObjectImage()
    {
        PersistenceBroker broker = getBroker();
        try
        {
            // if an image already exists we
            // replace the Identity too, maybe a temporary
            // used PK value was replaced by the real one,
            // see in docs SequenceManagerNativeImpl
            if(beforeImage != null)
            {
                oid = broker.serviceIdentity().buildIdentity(myObj);
            }
            if(currentImage != null)
            {
                beforeImage = currentImage;
            }
            else
            {
                if(beforeImage == null)
                {
                    beforeImage = buildObjectImage(getBroker());
                }
            }
            currentImage = null;
            hasChanged = null;
        }
        catch(Exception ex)
        {
            beforeImage = null;
            currentImage = null;
            hasChanged = null;
            log.error("Can't refresh object image", ex);
            throw new org.odmg.ClassNotPersistenceCapableException(ex.toString());
        }
    }

    public Identity getIdentity()
    {
        return oid;
    }

    /**
     * returns the managed object.
     */
    public Object getObject()
    {
        return myObj;
    }

    public void refreshObjectIfNeeded(Object obj)
    {
        if(this.myObj != obj)
        {
            this.myObj = obj;
        }
    }

    /**
     * We need to implement the Two-Phase Commit
     * protocol.
     *
     * beginCommit is where we say if we can or cannot
     * commit the transaction.  At the begining however,
     * we need to attain the after image so we can isolate
     * everything.
     *
     * We should issue the call against the database
     * at this point.  If we get a SQL Exception, we
     * should throw the org.odmg.TransactionAbortedException.
     *
     * We should also check to see if the object is
     * TransactionAware.  If so, we should give it a chance
     * to kill the transaction before we toss it to the
     * database.
     */
    public void beforeCommit()
    {
        if(myObj instanceof TransactionAware)
        {
            TransactionAware ta = (TransactionAware) myObj;
            ta.beforeCommit();
        }
    }

    /**
     * Method declaration
     */
    public void afterCommit()
    {
        if(myObj instanceof TransactionAware)
        {
            TransactionAware ta = (TransactionAware) myObj;
            ta.afterCommit();
        }
    }

    /**
     * Method declaration
     */
    public void beforeAbort()
    {
        if(myObj instanceof TransactionAware)
        {
            TransactionAware ta = (TransactionAware) myObj;
            ta.beforeAbort();
        }
    }

    /**
     * Method declaration
     */
    public void afterAbort()
    {
        if(myObj instanceof TransactionAware)
        {
            TransactionAware ta = (TransactionAware) myObj;
            ta.afterAbort();
        }
    }

    /**
     * buildObjectImage() will return the image of the Object.
     */
    private Map buildObjectImage(PersistenceBroker broker) throws PersistenceBrokerException
    {
        Map fieldValues = new HashMap();
        ClassDescriptor mif = broker.getClassDescriptor(getObject().getClass());
        //System.out.println("++++ build image: " + getObject());

        /**
         * MBAIRD
         * 1. register all 1:1 references
         * field changes to 1:1 mapped objects should also be registered in the map,
         * so that alterations to those related objects will trigger an object to be
         * marked "dirty", otherwise attaching or detaching a 1:1 referenced object will
         * not be updated in ODMG.
         */
        Iterator iter = mif.getObjectReferenceDescriptors().iterator();
        ObjectReferenceDescriptor rds = null;
        while(iter.hasNext())
        {
            Object referenceObject = null;
            EqualsRefHelper erh;
            rds = (ObjectReferenceDescriptor) iter.next();

            /*
             * synchronize on myObj so the ODMG-layer can take a snapshot only of
             * fully cached (i.e. with all references + collections) objects
             */
            synchronized(myObj)
            {
                referenceObject = rds.getPersistentField().get(myObj);
            }
            /**
             * MBAIRD
             * In the case of a proxy, we check if it has been materialized
             * if it's been materialized, we put it in the map, because it could change.
             * if it hasn't been materialized, it hasn't changed.
             *
             * Also handles virtual proxies.
             *
             * arminw:
             * wrap Object or Identity with a helper class. The main object will get
             * dirty when the 1:1 reference change: add or replaced by another object or deleted
             */
            IndirectionHandler handler = ProxyHelper.getIndirectionHandler(referenceObject);
            // if it is a not materialized proxy, use the Identity
            if(handler != null)
            {
                erh = handler.alreadyMaterialized()
                        ? new EqualsRefHelper(handler.getRealSubject())
                        : new EqualsRefHelper(handler.getIdentity());
            }
            else
            {
                erh = new EqualsRefHelper(referenceObject);
            }
            /*
            arminw:
            if object was serialized and anonymous FK are used in the main object, the FK
            values are null, we have to refresh (re-assign) this values before building field images
            */
            if(handler == null && referenceObject != null && BrokerHelper.hasAnonymousKeyReference(mif, rds))
            {
                getBroker().serviceBrokerHelper().link(myObj, rds, false);
            }
            /*
             register the Identity for 1:1 relations only, if change we have
             to update the main object
             */
            fieldValues.put(rds, erh);
        }


        /**
         * MBAIRD
         * 2. register all fields of object that aren't collections or references
         */
        FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
        for(int i = 0; i < fieldDescs.length; i++)
        {
            FieldDescriptor fld = fieldDescs[i];
            // map copies of all field values
            Object value = fld.getPersistentField().get(myObj);
            // get the real sql type value
            value = fld.getFieldConversion().javaToSql(value);
            // make copy of the sql type value
            value = fld.getJdbcType().getFieldType().copy(value);
            // buffer in image the field name and the sql type value
            // wrapped by a helper class
            fieldValues.put(fld.getPersistentField().getName(), new EqualsFieldHelper(fld.getJdbcType().getFieldType(), value));
        }


        /**
         * MBAIRD
         * 3. now let's register the collection descriptors
         * How do we handle proxied collections and collections of proxies
         */
        Iterator collections = mif.getCollectionDescriptors().iterator();
        CollectionDescriptor cds = null;
        while(collections.hasNext())
        {
            cds = (CollectionDescriptor) collections.next();
            Object collectionOrArray = cds.getPersistentField().get(myObj);
            EqualsColHelper ech = new EqualsColHelper(cds, collectionOrArray);
            fieldValues.put(cds, ech);
        }
        return fieldValues;
    }

    /**
     * returns the Modification-state.
     * @return org.apache.ojb.server.states.ModificationState
     */
    public ModificationState getModificationState()
    {
        return modificationState;
    }

    /**
     * returns true if the underlying Object needs an INSERT statement, else returns false.
     */
    public boolean needsInsert()
    {
        return this.getModificationState().needsInsert();
    }

    /**
     * returns true if the underlying Object needs an UPDATE statement, else returns false.
     */
    public boolean needsUpdate()
    {
        return this.getModificationState().needsUpdate();
    }

    /**
     * returns true if the underlying Object needs an UPDATE statement, else returns false.
     */
    public boolean needsDelete()
    {
        return this.getModificationState().needsDelete();
    }

    /**
     * sets the initial MoificationState of the wrapped object myObj. The initial state will be StateNewDirty if myObj
     * is not persisten already. The state will be set to StateOldClean if the object is already persistent.
     */
    private void prepareInitialState(boolean isNewObject)
    {
        // determine appropriate modification state
        ModificationState initialState = null;
        if(isNewObject)
        {
            // if object is not already persistent it must be marked as new
            // it must be marked as dirty because it must be stored even if it will not modified during tx
            initialState = StateNewDirty.getInstance();
        }
        else if(isDeleted(oid))
        {
            // if object is already persistent it will be marked as old.
            // it is marked as dirty as it has been deleted during tx and now it is inserted again,
            // possibly with new field values.
            initialState = StateOldDirty.getInstance();
        }
        else
        {
            // if object is already persistent it will be marked as old.
            // it is marked as clean as it has not been modified during tx already
            initialState = StateOldClean.getInstance();
        }
        // remember it:
        modificationState = initialState;
    }

    /**
     * Checks if the object with the given identity has been deleted
     * within the transaction.
     * @param id The identity
     * @return true if the object has been deleted
     * @throws PersistenceBrokerException
     */
    public boolean isDeleted(Identity id)
    {
        ObjectEnvelope envelope = buffer.getByIdentity(id);

        return (envelope == null ? false : envelope.needsDelete());
    }

    /**
     * set the Modification state to a new value. Used during state transitions.
     * @param newModificationState org.apache.ojb.server.states.ModificationState
     */
    public void setModificationState(ModificationState newModificationState)
    {
        if(newModificationState != modificationState)
        {
            if(LoggerFactory.getDefaultLogger().isDebugEnabled())
            {
                LoggerFactory.getDefaultLogger().debug("object state transition for object " + this.oid + " ("
                        + modificationState + " --> " + newModificationState + ")");
            }
            modificationState = newModificationState;
        }
    }

    /**
     * returns a String representation.
     * @return java.lang.String
     */
    public String toString()
    {
        ToStringBuilder buf = new ToStringBuilder(this);
        buf.append("Identity", oid)
            .append("ModificationState", modificationState.toString());
        return buf.toString();
    }

    /**
     * checks whether object and internal clone differ and returns true if so, returns false else.
     * @return boolean
     */
    public boolean hasChanged(PersistenceBroker broker)
    {
        try
        {
            currentImage = getCurrentImage();
        }
        catch(Exception e)
        {
            LoggerFactory.getDefaultLogger().warn("Could not verify object changes, return hasChanged 'true'", e);
        }
        hasChanged = (beforeImage != null && beforeImage.equals(currentImage) ? Boolean.FALSE : Boolean.TRUE);

        return hasChanged.booleanValue();
    }

    /**
     * Mark new or deleted reference elements
     * @param broker
     */
    void markReferenceElements(PersistenceBroker broker)
    {
        // these cases will be handled by ObjectEnvelopeTable#cascadingDependents()
        if(getModificationState().needsInsert() || getModificationState().needsDelete()) return;

        Map oldImage = getBeforeImage();
        Map newImage = getCurrentImage();

        Iterator iter = newImage.entrySet().iterator();
        while (iter.hasNext())
        {
            Map.Entry entry = (Map.Entry) iter.next();
            // CollectionDescriptor extends ObjectReferenceDescriptor
            // we have to search for XXXDescriptor
            if(entry.getKey() instanceof ObjectReferenceDescriptor)
            {
                if (entry.getKey() instanceof CollectionDescriptor)
                {
                    CollectionDescriptor cds = (CollectionDescriptor) entry.getKey();
                    EqualsColHelper oldEch = (EqualsColHelper) oldImage.get(cds);
                    EqualsColHelper newEch = (EqualsColHelper) entry.getValue();
//System.out.println("");
//System.out.println("mark-oldEch: " + oldEch);
//System.out.println("mark-newEch: " + newEch);

                    markDelete(cds, oldEch, newEch);
                    markNew(cds, oldEch, newEch);
                }
                else
                {
                    /*
                    check for new 1:1 reference object
                    */
                    ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) entry.getKey();
                    EqualsRefHelper oldEh = (EqualsRefHelper) oldImage.get(rds);
                    EqualsRefHelper newEh = (EqualsRefHelper) newImage.get(rds);
                    if(!oldEh.equals(newEh))
                    {
                        // the main objects needs link/unlink of the FK to 1:1 reference,
                        // so mark this dirty
                        setModificationState(getModificationState().markDirty());
                        // if the new reference helper value is not null
                        // Lock the object, because it can be a new unregistered object.
                        // in other cases it can be an unmaterialized proxy and we
                        // don't need to materialize
                        if(newEh.value != null)
                        {
                            // if the object is already registered, OJB knows about
                            // else lock and register object, get read lock, because we
                            // don't know if the object is new or moved from an existing other object
                            ObjectEnvelope oe = buffer.getByIdentity(newEh.oid);
                            if(oe == null)
                            {
                                RuntimeObject rt = new RuntimeObject(newEh.value, getTx());
                                getTx().lockAndRegister(rt, TransactionExt.READ, false);
                            }
                            // in any case we need to link the main object
                            addLinkOneToOne(rds, false);
                        }
                        // if the new image doesn't contain a reference object, the
                        // reference is deleted, so lookup the old reference object
                        // and mark for delete
                        else if(newEh.isNull())
                        {
                            ObjectEnvelope oldRefMod = buffer.getByIdentity(oldEh.oid);
                            // only delete when the reference wasn't assigned with another object
                            if(!buffer.isNewAssociatedObject(oldEh.oid))
                            {
                                // if cascading delete is enabled, remove the 1:1 reference
                                // because it was removed from the main object
                                if(buffer.getTransaction().cascadeDeleteFor(rds))
                                {
                                    oldRefMod.setModificationState(oldRefMod.getModificationState().markDelete());
                                }
                                // in any case we have to unlink the main object
                                addLinkOneToOne(rds, true);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Mark object for delete if it's not available in the new Collection.
     */
    private void markDelete(CollectionDescriptor cds, EqualsColHelper oldEch, EqualsColHelper newEch)
    {
        if (oldEch.references.size() == 0)
        {
            return;
        }
        Iterator oldIter = oldEch.references.entrySet().iterator();
        Map newRefs = newEch.references;
        while (oldIter.hasNext())
        {
            Map.Entry entry = (Map.Entry) oldIter.next();
            Identity oldOid = (Identity) entry.getKey();
            if (!newRefs.containsKey(oldOid) && newEch.status != IS_UNMATERIALIZED_PROXY)
            {
                ObjectEnvelope mod = buffer.getByIdentity(oldOid);
                // if this object is associated with another object it's
                // not allowed to remove it
                if(!buffer.isNewAssociatedObject(oldOid))
                {
                    if(mod != null)
                    {
                        boolean cascade = buffer.getTransaction().cascadeDeleteFor(cds);
                        if(cascade)
                        {
                            mod.setModificationState(mod.getModificationState().markDelete());
                            buffer.addForDeletionDependent(mod);
                        }
                        if(cds.isMtoNRelation())
                        {
                            buffer.addM2NUnlinkEntry(cds, getObject(), entry.getValue());
                        }
                        else
                        {
                            // when cascade 'true' we remove all dependent objects, so no need to unlink
                            if(!cascade)
                            {
                                mod.setModificationState(mod.getModificationState().markDirty());
                                mod.addLinkOneToN(cds, getObject(), true);
                            }
                        }
                    }
                    else
                    {
                        throw new ImageExcetion("Unexpected behaviour, unregistered object to delete: "
                                + oldOid + ", main object is " + getIdentity());
                    }
                }
            }
        }
    }

    /**
     * Mark object for insert if it's not available in the old Collection.
     */
    private void markNew(CollectionDescriptor cds, EqualsColHelper oldEch, EqualsColHelper newEch)
    {
        if (newEch.references.size() == 0)
        {
            return;
        }

        Iterator newIter = newEch.references.entrySet().iterator();
        Map oldRefs = oldEch.references;
        while (newIter.hasNext())
        {
            Map.Entry entry = (Map.Entry) newIter.next();
            Identity newOid = (Identity) entry.getKey();
            Object newObj = entry.getValue();

            /*
            search for new objects: if in the old reference collection an object
            of the new reference collection is not contained
            */
            if ((oldRefs == null) || !oldRefs.containsKey(newOid))
            {
                ObjectEnvelope mod = buffer.getByIdentity(newOid);
                if(mod == null) mod = buffer.get(newObj, true);
                // if the object was deleted in an previous action, mark as new
                // to avoid deletion, else mark object as dirty to assign the FK of
                // the main object
                if(mod.needsDelete())
                {
                    mod.setModificationState(mod.getModificationState().markNew());
                }
                else
                {
                    mod.setModificationState(mod.getModificationState().markDirty());
                }
                // buffer this object as "new" in a list to prevent deletion
                // when object was moved from one collection to another
                buffer.addNewAssociatedIdentity(newOid);
                // new referenced object found, so register all m:n relation for "linking"
                if(cds.isMtoNRelation())
                {
                    buffer.addM2NLinkEntry(cds, getObject(), newObj);
                }
                if(mod.needsInsert())
                {
                    buffer.addForInsertDependent(mod);
                }
                // we have to link the new object
                mod.addLinkOneToN(cds, getObject(), false);
            }
        }
    }

    public void doUpdate()
    {
        performLinkEntries();
        getBroker().store(getObject(), ObjectModificationDefaultImpl.UPDATE);
    }

    public void doInsert()
    {
        performLinkEntries();
        getBroker().store(getObject(), ObjectModificationDefaultImpl.INSERT);
    }

    public void doDelete()
    {
        getBroker().delete(getObject());
    }

    public void doEvictFromCache()
    {
        getBroker().removeFromCache(getIdentity());
    }

    public boolean isWriteLocked()
    {
        return writeLocked;
    }

    public void setWriteLocked(boolean writeLocked)
    {
        this.writeLocked = writeLocked;
    }

    ClassDescriptor getClassDescriptor()
    {
        return getBroker().getClassDescriptor(ProxyHelper.getRealClass(getObject()));
    }

    void addLinkOneToOne(ObjectReferenceDescriptor ord, boolean unlink)
    {
        LinkEntry entry = new LinkEntryOneToOne(ord, getObject(), unlink);
        linkEntryList.add(entry);
        //setModificationState(getModificationState().markDirty());
    }

    void addLinkOneToN(CollectionDescriptor col, Object source, boolean unlink)
    {
        LinkEntry entry = new LinkEntryOneToN(source, col, getObject(), unlink);
        linkEntryList.add(entry);
        //setModificationState(getModificationState().markDirty());
    }

    private void performLinkEntries()
    {
        PersistenceBroker broker = getBroker();
        for(int i = 0; i < linkEntryList.size(); i++)
        {
            LinkEntry linkEntry = (LinkEntry) linkEntryList.get(i);
            linkEntry.execute(broker);
        }
    }


    //====================================================
    // inner class
    //====================================================
    /**
     * Help to compare field values.
     */
    abstract class EqualsBase
    {
        abstract void close();
        abstract void prepareForCompare();
    }

    //====================================================
    // inner class
    //====================================================
    /**
     * Help to compare field values.
     */
    class EqualsFieldHelper extends EqualsBase
    {
        FieldType type;
        Object value;

        public EqualsFieldHelper(FieldType type, Object value)
        {
            this.type = type;
            this.value = value;
        }

        void close()
        {
        }

        void prepareForCompare()
        {
        }

        public boolean equals(Object valueNew)
        {
            boolean result = false;
            if(this==valueNew)
            {
                result = true;
            }
            else
            {
                if(valueNew instanceof EqualsFieldHelper)
                {
                    result = type.equals(value, ((EqualsFieldHelper) valueNew).value);
                }
            }
//            if(!result)
//            {
//                System.out.println("** changed field: " + getIdentity() + ", this="+this + ", other=" + valueNew);
//            }
            return result;
        }

        public String toString()
        {
            return "EqualsFieldHelper[type=" + type + "->value=" + value + "]";
        }
    }

    //====================================================
    // inner class
    //====================================================
    /**
     * Help to compare 1:1 references of the main object.
     */
    class EqualsRefHelper extends EqualsBase
    {
        Identity oid;
        Object value;

        public EqualsRefHelper(Object refObject)
        {
            this.value = refObject;
        }

        public EqualsRefHelper(Identity oid)
        {
            this.oid = oid;
        }

        void close()
        {
        }

        void prepareForCompare()
        {
        }

        /**
         * The reference is <em>null</em> when both Identity and Object attribute
         * is null.
         */
        public boolean isNull()
        {
            return oid == null && value == null;
        }

        public boolean equals(Object toCompare)
        {
            boolean result = false;
            if(this==toCompare)
            {
                result = true;
            }
            else
            {
                if(toCompare instanceof EqualsRefHelper)
                {
                    EqualsRefHelper other = (EqualsRefHelper) toCompare;
                    if(oid == null)
                    {
                        if(value != null)
                        {
                            oid = getBroker().serviceIdentity().buildIdentity(value);
                        }
                    }
                    if(other.oid == null)
                    {
                        if(other.value != null)
                        {
                            other.oid = getBroker().serviceIdentity().buildIdentity(other.value);
                        }
                    }
                    // return oid != null ? oid.equals(other.oid) : other.oid == null;
                    result = oid != null ? oid.equals(other.oid) : other.oid == null;
                }
            }
//            if(!result)
//            {
//                System.out.println("** changed 1:1: " + getIdentity() + ", ref: " + oid);
//            }
            return result;
        }
    }

    //====================================================
    // inner class
    //====================================================
    /**
     * Help to compare 1:1 references of the main object.
     */
    class EqualsColHelper extends EqualsBase implements CollectionProxyListener
    {
        private CollectionDescriptor des;
        private CollectionProxy collectionHandler;
        private int status;
        private Map references;
       // private Collection proxyData;

        public EqualsColHelper(CollectionDescriptor des, Object collOrArray)
        {
            this.des = des;
            references = new HashMap();
            assignReferenceObject(collOrArray);
            //System.out.println("** Create " + this);
        }

        public String toString()
        {
            return new ToStringBuilder(this)
                    .append("main class", des.getClassDescriptor().getClassNameOfObject())
                    .append("reference name", des.getPersistentField().getName())
                    .append("reference class", des.getItemClassName())
                    .append("status", status)
                    .append("references", references)
                    .toString();
        }

        /**
         * Always returns true, because changes in 1:n or m:n relations
         * do not influence main object, no need for update main object
         * thus return always true.
         */
        public boolean equals(Object obj)
        {
            return (obj instanceof EqualsColHelper) ? true : false;
        }

        void prepareForCompare()
        {
        }

        void close()
        {
            if(collectionHandler != null)
            {
                collectionHandler.removeListener(this);
                collectionHandler = null;
            }
        }

        void assignReferenceObject(Object collOrArray)
        {
            CollectionProxy colProxy = ProxyHelper.getCollectionProxy(collOrArray);
            if(colProxy != null)
            {
                if(colProxy.isLoaded())
                {
                    status = IS_MATERIALIZED_PROXY;
                    /*
                    TODO: avoid dependency to CollectionProxyDefaultImpl
                    e.g. change CollectionProxy interface - CollectionProxy should
                    extend Collection to support Iterator
                    */
                    handleCollectionOrArray(((CollectionProxyDefaultImpl) colProxy).getData());
                }
                else
                {
                    status = IS_UNMATERIALIZED_PROXY;
// TODO: ObjectEnvelopeTable#register take care of proxy objects/collection, no need to handle proxy objects??
                    colProxy.addListener(this);
                    collectionHandler = colProxy;
                }
            }
            else
            {
                status = IS_MATERIALIZED_OBJECT;
                handleCollectionOrArray(collOrArray);
            }
        }

        public void beforeLoading(CollectionProxyDefaultImpl colProxy)
        {
            //noop
        }

        public void afterLoading(CollectionProxyDefaultImpl colProxy)
        {
            if(status == IS_UNMATERIALIZED_PROXY)
            {
                //System.out.println("**** materialize " + this + " ->> data: " + colProxy.getData());
                // reference to the proxy data
                handleMaterializedCollectionProxy(colProxy);
                status = IS_MATERIALIZED_PROXY;
                colProxy.removeListener(this);
                collectionHandler = null;
            }
        }

        void addReference(Identity oid, Object obj)
        {
            references.put(oid, obj);
        }

        void handleCollectionOrArray(Object collOrArray)
        {
            if(collOrArray == null) return;
            Iterator it = BrokerHelper.getCollectionIterator(collOrArray);
            Object obj;
            while(it.hasNext())
            {
                obj = it.next();
                addReference(getBroker().serviceIdentity().buildIdentity(obj), obj);
            }
        }

        void handleMaterializedCollectionProxy(CollectionProxyDefaultImpl colProxy)
        {
            if(colProxy.getData() == null || colProxy.getData().size() == 0)
            {
                // nothing to do
                return;
            }
            // if the object materialize outside of a running tx (should not happen), we have to lookup
            // a broker instance itself, so use PBCapsule to handle this
            PBCapsule capsule = new PBCapsule(colProxy.getBrokerKey(), getTx());
            try
            {
                PersistenceBroker broker = capsule.getBroker();
                Iterator it = colProxy.ojbIterator();
                Object tempObj;
                IndirectionHandler tempHandler;
                while(it.hasNext())
                {
                    tempObj = it.next();
                    // the referenced objects can be proxy objects too
                    tempHandler = ProxyHelper.getIndirectionHandler(tempObj);
                    if(tempHandler != null)
                    {
                        addReference(tempHandler.getIdentity(), tempObj);
                    }
                    else
                    {
                        addReference(broker.serviceIdentity().buildIdentity(tempObj), tempObj);
                    }
                }
            }
            finally
            {
                if(capsule != null) capsule.destroy();
            }
        }
    }


    //====================================================
    // inner class
    //====================================================
    /**
     * Thrown if something unexpected is happen when handling the
     * object images for state detection.
     */
    public static class ImageExcetion extends OJBRuntimeException
    {
        public ImageExcetion()
        {
        }

        public ImageExcetion(String msg)
        {
            super(msg);
        }

        public ImageExcetion(Throwable cause)
        {
            super(cause);
        }

        public ImageExcetion(String msg, Throwable cause)
        {
            super(msg, cause);
        }
    }


}
TOP

Related Classes of org.apache.ojb.odmg.ObjectEnvelope$ImageExcetion

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.