Package org.jpox.store.mapped.mapping

Source Code of org.jpox.store.mapped.mapping.CollectionMapping

/**********************************************************************
Copyright (c) 2002 Kelly Grizzle (TJDO) and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
2003 Andy Jefferson - coding standards
2005 Andy Jefferson - basic serialisation support
2005 Andy Jefferson - updated serialisation using SCOUtils methods
2005 Andy Jefferson - changed to allow the store to be set or list
    ...
**********************************************************************/
package org.jpox.store.mapped.mapping;

import java.util.Collection;

import org.jpox.StateManager;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.sco.SCO;
import org.jpox.sco.SCOCollection;
import org.jpox.sco.SCOContainer;
import org.jpox.sco.SCOUtils;
import org.jpox.store.exceptions.ReachableObjectNotCascadedException;
import org.jpox.store.mapped.expression.CollectionExpression;
import org.jpox.store.mapped.expression.CollectionLiteral;
import org.jpox.store.mapped.expression.CollectionSubqueryExpression;
import org.jpox.store.mapped.expression.LogicSetExpression;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.expression.ScalarExpression;
import org.jpox.store.mapped.query.Queryable;
import org.jpox.store.scostore.CollectionStore;
import org.jpox.util.JPOXLogger;

/**
* Mapping for Collection (Set/List) types.
*
* @version $Revision: 1.67 $
**/
public class CollectionMapping extends AbstractContainerMapping implements MappingCallbacks
{
    /**
     * Equality operator.
     * @param obj Object to compare against
     * @return Whether they are equal
     */
    public boolean equals(Object obj)
    {
        if (obj == this)
        {
            return true;
        }

        if (!obj.getClass().equals(getClass()))
        {
            return false;
        }

        CollectionMapping sm = (CollectionMapping)obj;

        return fmd.equals(sm.fmd) && storeMgr.equals(sm.storeMgr);
    }

    /**
     * Accessor for the Java type represented here.
     * @return The java type
     */
    public Class getJavaType()
    {
        return Collection.class;
    }

    // --------------- Implementation of MappingCallbacks -------------------

    /**
     * Method to be called after the insert of the owner class element.
     * @param sm StateManager of the owner
     */
    public void postInsert(StateManager sm)
    {
        Collection value = (Collection) sm.provideField(fmd.getAbsoluteFieldNumber());
        if (containerIsStoredInSingleColumn())
        {
            // Make sure the elements are ok for proceeding
            SCOUtils.validateObjectsForWriting(sm.getObjectManager(), value);
            return;
        }

        if (value == null)
        {
            // replace null collections with an empty SCO wrapper
            replaceFieldWithWrapper(sm, null, false, false);
            return;
        }

        Object[] collElements = value.toArray();
        if (!fmd.isCascadePersist())
        {
            // Field doesnt support cascade-persist so no reachability
            if (JPOXLogger.REACHABILITY.isDebugEnabled())
            {
                JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007006", fmd.getFullFieldName()));
            }

            // Check for any persistable elements that arent persistent
            for (int i=0;i<collElements.length;i++)
            {
                if (!sm.getObjectManager().getApiAdapter().isDetached(collElements[i]) &&
                    !sm.getObjectManager().getApiAdapter().isPersistent(collElements[i]))
                {
                    // Element is not persistent so throw exception
                    throw new ReachableObjectNotCascadedException(fmd.getFullFieldName(), collElements[i]);
                }
            }
        }
        else
        {
            // Reachability
            if (JPOXLogger.REACHABILITY.isDebugEnabled())
            {
                JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007007", fmd.getFullFieldName()));
            }

            // Check if some elements need attaching
            // TODO Investigate if we can just use the attachCopy route below and skip off this check
            boolean needsAttaching = false;
            for (int i=0;i<collElements.length;i++)
            {
                if (sm.getObjectManager().getApiAdapter().isDetached(collElements[i]))
                {
                    needsAttaching = true;
                    break;
                }
            }

            if (needsAttaching)
            {
                // Create a wrapper and attach the elements (and add the others)
                SCO collWrapper = replaceFieldWithWrapper(sm, null, false, false);
                collWrapper.attachCopy(value);
            }
            else
            {
                if (value.size() > 0)
                {
                    // Add the elements direct to the datastore
                    ((CollectionStore) storeMgr.getBackingStoreForField(sm.getObjectManager().getClassLoaderResolver(),fmd, value.getClass())).addAll(sm, value, 0);

                    // Create a SCO wrapper with the elements loaded
                    replaceFieldWithWrapper(sm, value, false, false);
                }
                else
                {
                    // Create a SCO wrapper
                    replaceFieldWithWrapper(sm, null, false, false);
                }
            }
        }
    }

    /**
     * Method to be called after any update of the owner class element.
     * @param sm StateManager of the owner
     */
    public void postUpdate(StateManager sm)
    {
        Collection value = (Collection) sm.provideField(fmd.getAbsoluteFieldNumber());
        if (containerIsStoredInSingleColumn())
        {
            // Make sure the elements are ok for proceeding
            SCOUtils.validateObjectsForWriting(sm.getObjectManager(), value);
            return;
        }

        if (value == null)
        {
            // remove any elements in the collection and replace it with an empty SCO wrapper
            ((CollectionStore) storeMgr.getBackingStoreForField(sm.getObjectManager().getClassLoaderResolver(),fmd,null)).clear(sm);

            replaceFieldWithWrapper(sm, null, false, false);
            return;
        }

        if (value instanceof SCOContainer)
        {
            // Already have a SCO value
            SCOContainer sco = (SCOContainer) value;
            if (sm.getObject() == sco.getOwner() && fieldName.equals(sco.getFieldName()))
            {
                // Flush any outstanding updates
                sco.flush();

                return;
            }

            if (sco.getOwner() != null)
            {
                throw new JPOXException(LOCALISER.msg("CollectionMapping.WrongOwnerError")).setFatal();
            }
        }

        if (!fmd.isCascadeUpdate())
        {
            // TODO Should this throw exception if the element doesnt exist?
            // User doesnt want to update by reachability
            if (JPOXLogger.REACHABILITY.isDebugEnabled())
            {
                JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007008", fmd.getFullFieldName()));
            }
            return;
        }
        if (JPOXLogger.REACHABILITY.isDebugEnabled())
        {
            JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007009", fmd.getFullFieldName()));
        }

        // Update the datastore with this value of collection (clear old elements and add new ones)
        // TODO Consider making this more efficient picking the ones to remove/add
        CollectionStore backingStore = ((CollectionStore) storeMgr.getBackingStoreForField(sm.getObjectManager().getClassLoaderResolver(),fmd, value.getClass()));

        backingStore.clear(sm);
        backingStore.addAll(sm, value, 0);

        // Replace the field with a wrapper containing these elements
        replaceFieldWithWrapper(sm, value, false, false);
    }

    /**
     * Method to be called before any delete of the owner class element.
     * @param sm StateManager of the owner
     */
    public void preDelete(StateManager sm)
    {
        if (containerIsStoredInSingleColumn())
        {
            // Field is stored with the main object so nothing to clean up
            return;
        }

        // makes sure field is loaded
        sm.getObjectManager().getApiAdapter().isLoaded(sm, fmd.getAbsoluteFieldNumber());
        Collection value = (Collection) sm.provideField(fmd.getAbsoluteFieldNumber());
        if (value == null)
        {
            return;
        }

        boolean isDependentElement = fmd.getCollection().isDependentElement();
        boolean hasJoin = (fmd.getJoinMetaData() != null);
        boolean hasFK = false;
        if (!hasJoin)
        {
            if (fmd.getElementMetaData() != null && fmd.getElementMetaData().getForeignKeyMetaData() != null)
            {
                // FK collection, using <element> FK spec
                hasFK = true;
            }
            else if (fmd.getForeignKeyMetaData() != null)
            {
                // FK collection, using <field> FK spec
                hasFK = true;
            }
            AbstractMemberMetaData[] relatedMmds = fmd.getRelatedMemberMetaData(sm.getObjectManager().getClassLoaderResolver());
            if (relatedMmds != null && relatedMmds[0].getForeignKeyMetaData() != null)
            {
                // FK collection (bidir), using <field> FK spec at other end
                hasFK = true;
            }
        }
        if (sm.getObjectManager().getOMFContext().getPersistenceConfiguration().getStringProperty("org.jpox.deletionPolicy").equals("JDO2"))
        {
            // JDO2 doesnt currently (2.0 spec) take note of foreign-key
            hasFK = false;
        }

        // TODO Why dont we just do clear here always ? THe backing store should take care of if nulling or deleting etc
        if (isDependentElement || hasJoin || !hasFK)
        {
            // Elements are either dependent (in which case we need to delete them) or theres a join (in which case
            // we need to remove the join entries), or there are no FKs specified (in which case we need to clean up)
            if (!(value instanceof SCO))
            {
                value = (Collection)sm.wrapSCOField(fmd.getAbsoluteFieldNumber(), value, false, false, true);
            }
            value.clear();
            ((SCOCollection)value).flush();
        }
    }

    // ------------------------------- JDOQL Query Methods --------------------------------------

    /**
     * Accessor for a literal representing this type.
     * @param qs The Query
     * @param value the value of this object in the literal
     * @return The literal
     */
    public ScalarExpression newLiteral(QueryExpression qs, Object value)
    {
        if (containerIsStoredInSingleColumn())
        {
            throw new JPOXUserException(LOCALISER.msg("041025", fmd.getFullFieldName())).setFatal();
        }
        if (value instanceof Queryable)
        {
            return new CollectionSubqueryExpression(qs, ((Queryable)value).newQueryStatement());
        }
        else
        {
            return new CollectionLiteral(qs, this, (Collection)value);
        }
    }

    /**
     * Accessor for a scalar expression involving this object.
     * @param qs The Query
     * @param te The table holding this object.
     * @return The expression
     */
    public ScalarExpression newScalarExpression(QueryExpression qs, LogicSetExpression te)
    {
        if (containerIsStoredInSingleColumn())
        {
            throw new JPOXUserException(LOCALISER.msg("041025", fmd.getFullFieldName())).setFatal();
        }
        return new CollectionExpression(qs, datastoreContainer.getIDMapping(), te, ((CollectionStore) storeMgr.getBackingStoreForField(qs.getClassLoaderResolver(),fmd,null)), fieldName);
    }
}
TOP

Related Classes of org.jpox.store.mapped.mapping.CollectionMapping

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.