Package org.datanucleus.store.mapped.scostore

Source Code of org.datanucleus.store.mapped.scostore.ElementContainerStore$ElementInfo

/**********************************************************************
Copyright (c) 2005 Andy Jefferson 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:
    ...
**********************************************************************/
package org.datanucleus.store.mapped.scostore;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.CollectionMetaData;
import org.datanucleus.metadata.DiscriminatorStrategy;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.FieldValues;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.StoreManager;
import org.datanucleus.store.mapped.DatastoreClass;
import org.datanucleus.store.mapped.DatastoreContainerObject;
import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
import org.datanucleus.store.types.sco.SCOUtils;
import org.datanucleus.util.ClassUtils;

/**
* Representation of the store of an element-based container.
* This is used to represent either a collection or an array.
* There are the following types of situation that we try to cater for with respect to elements.
* <UL>
* <LI><B>element-type</B> is PC with "new-table" or "superclass-table" inheritance.
* In this case we will have
* <I>elementInfo</I> with 1 entry.</LI>
* <LI><B>element-type</B> is PC with "subclass-table" inheritance. In this case we will have <I>elementInfo</I>
* with "n" entries (1 for each subclass type with its own table). We also have <I>emd</I> being the MetaData
* for the element-type.</LI>
* <LI><B>element-type</B> is Reference type. In this case we will have <I>elementInfo</I> with "n" entries
* (1 for each implementation type).</LI>
* <LI><B>element-type</B> is non-PC. In this case we have no <I>elementInfo</I> and no <I>emd</I></LI>
* </UL>
*/
public abstract class ElementContainerStore extends BaseContainerStore
{
    /** Flag to set whether the iterator statement will use a discriminator or not. */
    protected boolean iterateUsingDiscriminator = false;

    /**
     * Information for the elements of this container.
     * When the "element-type" table is new-table, or superclass-table then there is 1 value here.
     * When the "element-type" table uses subclass-table, or when it is a reference type then there can be multiple.
     */
    protected ElementInfo[] elementInfo;

    /** Inner class wrapping the information required for a valid element type. */
    public static class ElementInfo
    {
        AbstractClassMetaData cmd; // MetaData for the element class
        DatastoreClass table; // Table storing the element

        /**
         * Constructor
         * @param cmd the AbstractClassMetaData
         * @param table the DatastoreClass
         */
        public ElementInfo(AbstractClassMetaData cmd, DatastoreClass table)
        {
            this.cmd = cmd;
            this.table = table;
        }

        /**
         * Accessor for the class name
         * @return the class name
         */
        public String getClassName()
        {
            return cmd.getFullClassName();
        }

        /**
         * Accessor for the AbstractClassMetaData
         * @return the AbstractClassMetaData
         */
        public AbstractClassMetaData getAbstractClassMetaData()
        {
            return cmd;
        }

        /**
         * Accessor for the table of the element
         * @return the DatastoreClass
         */
        public DatastoreClass getDatastoreClass()
        {
            return table;
        }

        /**
         * Accessor the discriminator strategy of the element
         * @return the strategy for the discriminator
         */
        public DiscriminatorStrategy getDiscriminatorStrategy()
        {
            return cmd.getDiscriminatorStrategyForTable();
        }

        /**
         * Accessor the discriminator mapping of the element (in its table)
         * @return the JavaTypeMapping for the discriminator
         */
        public JavaTypeMapping getDiscriminatorMapping()
        {
            return table.getDiscriminatorMapping(false);
        }
    }

    /** MetaData for the "element-type" class. Not used for reference types since no metadata is present for the declared type. */
    protected AbstractClassMetaData emd;

    /** Table containing the link between owner and element. Not set when using FK relations. */
    protected DatastoreContainerObject containerTable;

    /** Mapping for the element. */
    protected JavaTypeMapping elementMapping;

    /** Type of the element. */
    protected String elementType;

    /** Whether the elements are embedded. */
    protected boolean elementsAreEmbedded;

    /** Whether the elements are serialised. */
    protected boolean elementsAreSerialised;

    /** Whether the element is of a persistent-interface (defined using "<interface>") type. */
    protected boolean elementIsPersistentInterface = false;

    /**
     * Mapping for an ordering column to allow for duplicates in the container.
     * Can also be used for ordering elements in a List/array.
     * Can also be used where we have an embedded object and so need to form the PK with something.
     */
    protected JavaTypeMapping orderMapping;

    /** Optional mapping to distinguish elements of one collection from another when sharing the join table. */
    protected JavaTypeMapping relationDiscriminatorMapping;

    /** Value to use to discriminate between elements of this collection from others using the same join table. */
    protected String relationDiscriminatorValue;

    /** ClassLoader resolver. */
    protected ClassLoaderResolver clr;

    /** Specialization. */
    protected final ElementContainerStoreSpecialization specialization;

    /**
     * Constructor.
     * @param storeMgr Manager for the store
     * @param clr ClassLoader resolver
     */
    protected ElementContainerStore(StoreManager storeMgr, ClassLoaderResolver clr, ElementContainerStoreSpecialization elementContainerStoreSpecialization)
    {
        super(storeMgr);
        this.clr = clr;
        this.specialization = elementContainerStoreSpecialization;
    }

    public ElementInfo[] getElementInfo()
    {
        return elementInfo;
    }

    public JavaTypeMapping getElementMapping()
    {
        return elementMapping;
    }

    public JavaTypeMapping getOrderMapping()
    {
        return orderMapping;
    }

    public JavaTypeMapping getRelationDiscriminatorMapping()
    {
        return relationDiscriminatorMapping;
    }

    public String getRelationDiscriminatorValue()
    {
        return relationDiscriminatorValue;
    }

    public DatastoreContainerObject getContainerTable()
    {
        return containerTable;
    }

    public AbstractClassMetaData getEmd()
    {
        return emd;
    }

    public boolean isElementsAreSerialised()
    {
        return elementsAreSerialised;
    }

    public boolean isElementsAreEmbedded()
    {
        return elementsAreEmbedded;
    }

    /**
     * Convenience method to find the element information relating to the element type.
     * Used specifically for the "element-type" of a collection/array to find the elements
     * which have table information. Not used for reference types.
     * @return Element information relating to the element type
     */
    protected ElementInfo[] getElementInformationForClass()
    {
        ElementInfo[] info = null;
        DatastoreClass tbl;
        String[] clsNames;
        if (!clr.classForName(elementType).isInterface())
        {
            tbl = storeMgr.getDatastoreClass(elementType, clr);
            clsNames = new String[] {elementType};
        }
        else
        {
            clsNames = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(elementType, clr);
            tbl = storeMgr.getDatastoreClass(clsNames[0], clr);
        }
        if (tbl == null)
        {
            AbstractClassMetaData[] subclassCmds = storeMgr.getClassesManagingTableForClass(emd, clr);
            info = new ElementInfo[subclassCmds.length];
            for (int i=0;i<subclassCmds.length;i++)
            {
                DatastoreClass table = storeMgr.getDatastoreClass(subclassCmds[i].getFullClassName(), clr);
                info[i] = new ElementInfo(subclassCmds[i], table);
            }
        }
        else
        {
            info = new ElementInfo[clsNames.length];
            for (int i=0; i<clsNames.length; i++)
            {
                AbstractClassMetaData cmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(clsNames[i], clr);
                DatastoreClass table = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
                info[i] = new ElementInfo(cmd, table);
            }
        }
        return info;
    }

    /**
     * Accessor for whether the store has an order mapping, to allow for duplicates or ordering.
     * @return Whether an order mapping is present.
     */
    public boolean hasOrderMapping()
    {
        return (orderMapping != null);
    }

    /**
     * Method to validate an element against the accepted type.
     * @param clr The ClassLoaderResolver
     * @param element The element to validate
     * @return Whether it is valid.
     **/
    protected boolean validateElementType(ClassLoaderResolver clr, Object element)
    {
        if (element == null)
        {
            return true;
        }

        Class primitiveElementClass = ClassUtils.getPrimitiveTypeForType(element.getClass());
        if (primitiveElementClass != null)
        {
           
            // Allow for the element type being primitive, and the user wanting to store its wrapper
            String elementTypeWrapper = elementType;
            Class elementTypeClass = clr.classForName(elementType);
            if (elementTypeClass.isPrimitive())
            {
                elementTypeWrapper = ClassUtils.getWrapperTypeForPrimitiveType(elementTypeClass).getName();
            }
            return clr.isAssignableFrom(elementTypeWrapper, element.getClass());
        }
        return clr.isAssignableFrom(elementType, element.getClass());
    }

    /**
     * Method to check if an element is already persistent or is persistent but managed by
     * a different persistence manager.
     * @param sm The state manager of this owner
     * @param element The element
     * @return Whether it is valid for reading.
     */
    protected boolean validateElementForReading(ObjectProvider sm, Object element)
    {
        if (!validateElementType(sm.getExecutionContext().getClassLoaderResolver(), element))
        {
            return false;
        }

        if (element != null && !elementsAreEmbedded && !elementsAreSerialised)
        {
            ExecutionContext ec = sm.getExecutionContext();
            if ((!ec.getApiAdapter().isPersistent(element) ||
                 ec != ec.getApiAdapter().getExecutionContext(element)) && !ec.getApiAdapter().isDetached(element))
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Method to check if an element is already persistent, or is managed by a different
     * Persistencemanager. If not persistent, this will persist it.
     * @param ownerOP The ObjectProvider of this owner
     * @param element The element
     * @param fieldValues any initial field values to use if persisting the element
     * @return Whether the element was persisted during this call
     */
    protected boolean validateElementForWriting(ObjectProvider ownerOP, Object element, FieldValues fieldValues)
    {
        // Check the element type for this collection
        if (!elementIsPersistentInterface &&
            !validateElementType(ownerOP.getExecutionContext().getClassLoaderResolver(), element))
        {
            throw new ClassCastException(LOCALISER.msg("056033", element.getClass().getName(),
                ownerMemberMetaData.getFullFieldName(), elementType));
        }

        boolean persisted = false;
        if (elementsAreEmbedded || elementsAreSerialised)
        {
            // Element is embedded/serialised so has no id
        }
        else
        {
            ExecutionContext ec = ownerOP.getExecutionContext();
            ObjectProvider elementSM = ec.findObjectProvider(element);
            if (elementSM != null && elementSM.isEmbedded())
            {
                // Element is already with StateManager and is embedded in another field!
                throw new NucleusUserException(LOCALISER.msg("056028",
                    ownerMemberMetaData.getFullFieldName(), element));
            }

            persisted = SCOUtils.validateObjectForWriting(ownerOP, element, fieldValues);
        }
        return persisted;
    }

    /**
     * Accessor for an iterator through the container elements.
     * @param ownerSM State Manager for the container.
     * @return The Iterator
     */
    public abstract Iterator iterator(ObjectProvider ownerSM);

    /**
     * Clear the association from owner to all elements.
     * Provides cascade-delete when the elements being deleted are PC types.
     * @param ownerSM State Manager for the container.
     */
    public void clear(ObjectProvider ownerSM)
    {
        Collection dependentElements = null;
        CollectionMetaData collmd = ownerMemberMetaData.getCollection();
        boolean dependent = collmd.isDependentElement();
        if (ownerMemberMetaData.isCascadeRemoveOrphans())
        {
            dependent = true;
        }
        if (dependent && !collmd.isEmbeddedElement() && !collmd.isSerializedElement())
        {
            // Retain the dependent elements that need deleting after clearing
            dependentElements = new HashSet();
            Iterator iter = iterator(ownerSM);
            while (iter.hasNext())
            {
                dependentElements.add(iter.next());
            }
        }
        specialization.executeClear(ownerSM, this);

        // Cascade-delete
        if (dependentElements != null && dependentElements.size() > 0)
        {
            Iterator iter = dependentElements.iterator();
            while (iter.hasNext())
            {
                Object obj = iter.next();
                if (ownerSM.getExecutionContext().getApiAdapter().isDeleted(obj))
                {
                    // Element is tagged for deletion so will be deleted at flush(), and we dont need it immediately
                }
                else
                {
                    ownerSM.getExecutionContext().deleteObjectInternal(obj);
                }
            }
        }
    }


    /**
     * Method to return the size of the container.
     * @param sm The state manager.
     * @return The size.
     **/
    public int size(ObjectProvider sm)
    {
        return specialization.getSize(sm, this);
    }
}
TOP

Related Classes of org.datanucleus.store.mapped.scostore.ElementContainerStore$ElementInfo

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.