Package org.jpox.store.rdbms.scostore

Source Code of org.jpox.store.rdbms.scostore.MapEntrySetStore

/**********************************************************************
Copyright (c) 2002 Mike Martin (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 - allow for embedded keys/values in join table
2005 Andy Jefferson - add ROF extraction of key/value in same iterator stmt
    ...
**********************************************************************/
package org.jpox.store.rdbms.scostore;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map.Entry;

import org.jpox.ClassLoaderResolver;
import org.jpox.ManagedConnection;
import org.jpox.ObjectManager;
import org.jpox.StateManager;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.store.mapped.DatastoreContainerObject;
import org.jpox.store.mapped.DatastoreIdentifier;
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.mapping.EmbeddedKeyPCMapping;
import org.jpox.store.mapped.mapping.EmbeddedValuePCMapping;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.Mappings;
import org.jpox.store.mapped.mapping.SerialisedPCMapping;
import org.jpox.store.mapped.mapping.SerialisedReferenceMapping;
import org.jpox.store.query.ResultObjectFactory;
import org.jpox.store.rdbms.SQLController;
import org.jpox.store.rdbms.SQLWarnings;
import org.jpox.store.rdbms.mapping.RDBMSMapping;
import org.jpox.store.rdbms.table.JoinTable;
import org.jpox.store.rdbms.table.MapTable;
import org.jpox.store.scostore.MapStore;
import org.jpox.store.scostore.SetStore;

/**
* Representation of backing store for a MapEntry Set.
*
* @version $Revision: 1.54 $
**/
public class MapEntrySetStore extends BaseContainerStore implements SetStore
{
    /** Table containing the key and value forming the entry. */
    protected DatastoreContainerObject setTable;

    /** The backing store for the Map. */
    protected MapStore mapStore;

    /** Mapping for the key (if not PC(embedded)). */
    protected JavaTypeMapping keyMapping;

    /** Mapping for the value (if not PC(embedded)). */
    protected JavaTypeMapping valueMapping;

    /** The key type. */
    protected String keyType;

    /** The value type */
    protected String valueType;

    /** MetaData for the key class. */
    protected AbstractClassMetaData kmd;

    /** MetaData for the value class. */
    protected AbstractClassMetaData vmd;

    protected String iteratorStmt;
    protected String sizeStmt;

    protected ClassLoaderResolver clr;

    /**
     * Constructor when using a JoinTable map.
     * @param mapTable Table for the map
     * @param mapStore Store in use by the Map
     * @param clr ClassLoader resolver
     */
    public MapEntrySetStore(MapTable mapTable, MapStore mapStore, ClassLoaderResolver clr)
    {
        super(mapTable.getStoreManager());

        this.setTable = mapTable;
        this.mapStore = mapStore;

        keyType   = mapStore.getKeyType();
        valueType = mapStore.getValueType();

        ownerMapping = mapTable.getOwnerMapping();
        ownerMemberMetaData = mapTable.getOwnerFieldMetaData();
        keyMapping   = mapTable.getKeyMapping();
        valueMapping = mapTable.getValueMapping();

        kmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(keyType, clr);
        vmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(valueType, clr);

        this.clr = clr;
    }

    /**
     * Constructor when using a ForeignKey Map.
     * @param valueTable Table storing the values
     * @param ownerMapping Mapping to the owner
     * @param keyMapping Mapping to the key
     * @param valueMapping Mapping to the value
     * @param mapStore Store in use by the map.
     */
    public MapEntrySetStore(DatastoreContainerObject valueTable,
                            JavaTypeMapping ownerMapping,
                            JavaTypeMapping keyMapping,
                            JavaTypeMapping valueMapping,
                            MapStore mapStore)
    {
        super(valueTable.getStoreManager());

        this.setTable = valueTable;
        this.mapStore = mapStore;

        keyType   = mapStore.getKeyType();
        valueType = mapStore.getValueType();

        this.ownerMapping = ownerMapping;
        this.keyMapping   = keyMapping;
        this.valueMapping = valueMapping;
    }

    /**
     * Accessor for whether this store has an order mapping to allow for duplicates, or ordering.
     * @return Whether it has an order mapping.
     */
    public boolean hasOrderMapping()
    {
        return false;
    }

    /**
     * Method to update an embedded element.
     * @param sm State Manager of the owner
     * @param element The element to update
     * @param fieldNumber The number of the field to update
     * @param value The value
     * @return Whether the element was modified
     */
    public boolean updateEmbeddedElement(StateManager sm, Object element, int fieldNumber, Object value)
    {
        // Do nothing since of no use here
        return false;
    }

    /**
     * Method to return a size statement.
     * <PRE>
     * SELECT COUNT(*) FROM SETTABLE WHERE OWNERCOL=? AND KEYCOL IS NOT NULL
     * </PRE>
     * @return The size statement
     */
    private String getSizeStmt()
    {
        if (sizeStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("SELECT COUNT(*) FROM ");
            stmt.append(setTable.toString());
            stmt.append(" WHERE ");
            for (int i = 0; i < ownerMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(" AND ");
                }
                stmt.append(ownerMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping) ownerMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            // We dont accept null keys
            if (keyMapping != null)
            {
                for (int i=0; i<keyMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(keyMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" IS NOT NULL");
                }
            }
            sizeStmt = stmt.toString();
        }
        return sizeStmt;
    }

    /**
     * Method to return a statement to get the objects via an iterator.
     * This is only for use where we don't have an embedded PC key or an embedded
     * PC value.
     * <PRE>
     * SELECT KEYCOL, VALUECOL FROM SETTABLE WHERE OWNERCOL=? AND KEYCOL IS NOT NULL
     * </PRE>
     * @return The iterator statement
     */
    private String getIteratorStmt()
    {
        if (iteratorStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("SELECT ");
            if (keyMapping != null)
            {
                for (int i = 0; i < keyMapping.getNumberOfDatastoreFields(); i++)
                {
                    if (i > 0)
                    {
                        stmt.append(",");
                    }
                    stmt.append(keyMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                }
            }
            if (valueMapping != null)
            {
                for (int i = 0; i < valueMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(",");
                    stmt.append(valueMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                }
            }
            stmt.append(" FROM ");
            stmt.append(setTable.toString());
            stmt.append(" WHERE ");
            for (int i=0; i<ownerMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(" AND ");
                }
                stmt.append(ownerMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping) ownerMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            // We dont accept null keys
            if (keyMapping != null)
            {
                for (int i=0; i<keyMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(keyMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" IS NOT NULL");
                }
            }
            iteratorStmt = stmt.toString();
        }
        return iteratorStmt;
    }

    /**
     * Accessor for the element type.
     * @return The element type.
     */
    public String getElementType()
    {
        return EntryImpl.class.getName();
    }

    /**
     * Accessor for the owner mapping.
     * @return The owner mapping
     */
    public JavaTypeMapping getOwnerMapping()
    {
        return ownerMapping;
    }

    protected boolean validateElementType(Object element)
    {
        return element instanceof Entry;
    }

    /** Result object factory for extracting the map entries when using embedded PC key or value. */
    ResultObjectFactory rof = null;

    /**
     * Accessor for an iterator for the entries of the Map.
     * @param sm State manager for the owner
     * @return The iterator
     */
    public Iterator iterator(StateManager sm)
    {
        Iterator iter;

        String stmt = null;
        if (keyMapping != null && valueMapping != null)
        {
            // Key = PC, Non-PC(embedded)  Value = PC, Non-PC(embedded)
            stmt = getIteratorStmt();
        }
        else
        {
            // Key = PC(embedded) or Value = PC(embedded)
            // Generate the statement dynamically since we should select the FetchPlan fields only
            QueryExpression expr = dba.newQueryStatement(setTable, clr);
            ScalarExpression ownerExpr = ownerMapping.newScalarExpression(expr, expr.getMainTableExpression());
            ScalarExpression ownerVal = ownerMapping.newLiteral(expr, sm.getObject());
            expr.andCondition(ownerExpr.eq(ownerVal), true);

            rof = newResultObjectFactory(sm, expr, true);

            stmt = expr.toStatementText(false).toString();
        }

        try
        {
            ObjectManager om = sm.getObjectManager();
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, stmt);
                try
                {
                    if (keyMapping != null && valueMapping != null)
                    {
                        // Populate the owner
                        int jdbcPosition = 1;
                        jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                    }
                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        iter = new SetIterator(sm, rs);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException("Iteration request failed: " + stmt, e);
        }

        return iter;
    }

    /**
     * Inner class representing an iterator for the Set.
     **/
    private class SetIterator implements Iterator
    {
        private final StateManager sm;
        private final ObjectManager om;
        private final Iterator delegate;
        private Entry lastElement = null;

        /**
         * Constructor for iterating the Set of entry.
         * @param sm the StateManager
         * @param rs the ResultSet
         * @throws SQLException
         */
        public SetIterator(StateManager sm, ResultSet rs) throws SQLException
        {
            this.sm = sm;
            this.om = sm.getObjectManager();

            ArrayList results = new ArrayList();
            while (rs.next())
            {
                int jdbcPosition = 1;
                Object key = null;
                Object value = null;

                int ownerFieldNum = -1;
                if (setTable instanceof JoinTable)
                {
                    ownerFieldNum = ((JoinTable)setTable).getOwnerFieldMetaData().getAbsoluteFieldNumber();
                }

                if (keyMapping instanceof EmbeddedKeyPCMapping ||
                    keyMapping instanceof SerialisedPCMapping ||
                    keyMapping instanceof SerialisedReferenceMapping)
                {
                    key = keyMapping.getObject(om, rs, Mappings.getParametersIndex(jdbcPosition, keyMapping), sm, ownerFieldNum);
                }
                else
                {
                    key = keyMapping.getObject(om, rs, Mappings.getParametersIndex(jdbcPosition, keyMapping));
                }

                jdbcPosition += keyMapping.getNumberOfDatastoreFields();

                if (valueMapping instanceof EmbeddedValuePCMapping ||
                    valueMapping instanceof SerialisedPCMapping ||
                    valueMapping instanceof SerialisedReferenceMapping)
                {
                    value = valueMapping.getObject(om, rs, Mappings.getParametersIndex(jdbcPosition, valueMapping), sm, ownerFieldNum);
                }
                else
                {
                    value = valueMapping.getObject(om, rs, Mappings.getParametersIndex(jdbcPosition, valueMapping));
                }

                results.add(new EntryImpl(sm, key, value));
            }

            delegate = results.iterator();
        }

        public boolean hasNext()
        {
            return delegate.hasNext();
        }

        public Object next()
        {
            lastElement = (Entry)delegate.next();

            return lastElement;
        }

        public synchronized void remove()
        {
            if (lastElement == null)
            {
                throw new IllegalStateException("No entry to remove");
            }

            mapStore.remove(sm, lastElement.getKey());
            delegate.remove();

            lastElement = null;
        }
    }

    /**
     * Accessor for the size of the Map.
     * @param sm The state manager
     * @return The size
     **/
    public int size(StateManager sm)
    {
        int numRows;

        String stmt = getSizeStmt();
        try
        {
            ObjectManager om = sm.getObjectManager();
           
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, stmt);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        if (!rs.next())
                        {
                            throw new JPOXDataStoreException("Size request returned no result row: " + stmt);
                        }
                        numRows = rs.getInt(1);

                        SQLWarnings.log(rs);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException("Size request failed: " + stmt, e);
        }

        return numRows;
    }

    public boolean contains(StateManager sm, Object element)
    {
        if (!validateElementType(element))
        {
            return false;
        }
        Entry entry = (Entry)element;

        return mapStore.containsKey(sm, entry.getKey());
    }

    /**
     * Method to add an entry to the Map.
     * @param sm State Manager for the owner
     * @param element Entry to add
     * @return Whether it was added
     */
    public boolean add(StateManager sm, Object element, int size)
    {
        throw new UnsupportedOperationException("Cannot add to a map through its entry set");
    }

    /**
     * Method to add entries to the Map.
     * @param sm State Manager for the owner
     * @param elements Entries to add
     * @return Whether they were added
     */
    public boolean addAll(StateManager sm, Collection elements, int size)
    {
        throw new UnsupportedOperationException("Cannot add to a map through its entry set");
    }

    /**
     * Method to remove an entry from the Map.
     * @param sm State Manager for the owner
     * @param element Entry to remove
     * @return Whether it was removed
     */
    public boolean remove(StateManager sm, Object element, int size, boolean allowDependentField)
    {
        if (!validateElementType(element))
        {
            return false;
        }

        Entry entry = (Entry)element;
        Object removed = mapStore.remove(sm, entry.getKey());

        // NOTE: this may not return an accurate result if a null value is being removed
        return removed == null ? entry.getValue() == null : removed.equals(entry.getValue());
    }

    /**
     * Method to remove entries from the Map.
     * @param sm State Manager for the owner
     * @param elements Entries to remove
     * @return Whether they were removed
     */
    public boolean removeAll(StateManager sm, Collection elements, int size)
    {
        if (elements == null || elements.size() == 0)
        {
            return false;
        }

        Iterator iter=elements.iterator();
        boolean modified=false;
        while (iter.hasNext())
        {
            Object element=iter.next();
            Entry entry = (Entry)element;

            Object removed = mapStore.remove(sm, entry.getKey());

            // NOTE: this may not return an accurate result if a null value is being removed.
            modified = removed == null ? entry.getValue() == null : removed.equals(entry.getValue());
        }

        return modified;
    }

    /**
     * Method to clear the Map.
     * @param sm State Manager for the owner.
     */
    public void clear(StateManager sm)
    {
        mapStore.clear(sm);
    }

    /**
     * Inner class representing the entry.
     **/
    private class EntryImpl implements Entry
    {
        private final StateManager sm;
        private final Object key;
        private final Object value;

        /**
         * Entry constructor
         * @param sm the StateManager
         * @param key the key
         * @param value the value
         */
        public EntryImpl(StateManager sm, Object key, Object value)
        {
            this.sm = sm;
            this.key = key;
            this.value = value;
        }

        public int hashCode()
        {
            return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
        }

        public boolean equals(Object o)
        {
            if (o == this)
            {
                return true;
            }
            if (!(o instanceof Entry))
            {
                return false;
            }

            Entry e = (Entry)o;
            return (key == null ? e.getKey() == null : key.equals(e.getKey())) &&
                   (value == null ? e.getValue() == null : value.equals(e.getValue()));
        }

        /**
         * Accessor for the Key.
         * @return The Key.
         **/
        public Object getKey()
        {
            return key;
        }

        /**
         * Accessor for the Value.
         * @return The Value.
         **/
        public Object getValue()
        {
            return value;
        }

        /**
         * Mutator for the Value.
         * @param value The Value.
         * @return the previous value, or <code>null</code> if none.
         **/
        public Object setValue(Object value)
        {
            return mapStore.put(sm, key, value);
        }
    }

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

    /**
     * Method to return an object factory for processing map entry statements.
     * This type of statement allows both key and value to be extracted.
     * @param sm the StateManager
     * @param expr the QueryExpression
     * @param useFetchPlan whether to use the FetchPlan
     * @return nothing
     * @throws JPOXUserException unsupported method
     */
    public ResultObjectFactory newResultObjectFactory(StateManager sm, QueryExpression expr, boolean useFetchPlan)
    {
        throw new JPOXUserException("Cannot query sets obtained by Map.entrySet()");
    }

    /**
     * Method to return a new QueryStatement.
     * @param sm StateManager for the collection.
     * @param candidateClass The class to query
     * @param candidateAlias Alias for the candidate
     * @return The new QueryStatement.
     * @throws JPOXUserException unsupported method
     */
    public QueryExpression newQueryStatement(StateManager sm, String candidateClass, DatastoreIdentifier candidateAlias)
    {
        throw new JPOXUserException("Cannot query sets obtained by Map.entrySet()");
    }

    /**
     * Method to return an object factory for processing query statements.
     * @param sm State Manager for the collection.
     * @param stmt The Query Statement.
     * @param ignoreCache Whether to ignore the cache.
     * @param useFetchPlan whether to use the fetch plan to retrieve fields in the same query
     * @return The Persistent object factory.
     * @throws JPOXUserException unsupported method
     **/
    public ResultObjectFactory newResultObjectFactory(StateManager sm, QueryExpression stmt, boolean ignoreCache, boolean useFetchPlan)
    {
        throw new JPOXUserException("Cannot query sets obtained by Map.entrySet()");
    }

    /**
     * Create a subquery for the given query that joins a SetStore
     * element table to the owner table. This subquery can subsequently be used
     * in an EXISTS expression to determine whether a Collection is empty.
     * @param stmt The Query Statement.
     * @param ownerMapping the mapping for the owner.
     * @param ownerTe Table Expression for the owner
     * @param collectionTableAlias Alias for the "Collection" table.
     * @return A subquery for the given query that joins a SetStore
     *          element table to the owner table.
     * @throws JPOXUserException unsupported method
     */
    public QueryExpression getExistsSubquery(QueryExpression stmt,
                                            JavaTypeMapping ownerMapping,
                              LogicSetExpression ownerTe,
                                            DatastoreIdentifier collectionTableAlias)
    {
        throw new JPOXUserException("Cannot query sets obtained by Map.entrySet()");
    }

    /**
     * Create a subquery for the size() of the collection of entries.
     * @param stmt The Query Statement.
     * @param ownerMapping the mapping for the owner.
     * @param ownerTe Table Expression for the owner
     * @param collectionTableAlias Alias for the "Collection" table.
     * @return A subquery for the given query that joins a SetStore element table to the owner table.
     * @throws JPOXUserException unsupported method
     */
    public QueryExpression getSizeSubquery(QueryExpression stmt,
                                           JavaTypeMapping ownerMapping,
                                           LogicSetExpression ownerTe,
                                           DatastoreIdentifier collectionTableAlias)
    {
        throw new JPOXUserException("Cannot query sets obtained by Map.entrySet()");
    }

    /**
     * Method used in queries when contains() has been invoked.
     * @param stmt The Query Statement
     * @param ownerMapping the mapping for the owner.
     * @param ownerTe Table Expression for the owner
     * @param collectionTableAlias Alias for the "Collection" table.
     * @param filteredElementType The Class Type for the filtered element
     * @param elmExpr The Expression for the element
     * @param elementTableAlias The SQL alias to assign to the expression or to the element table.
     * @return expression to the join
     * @throws JPOXUserException unsupported method
     **/
    public ScalarExpression joinElementsTo(QueryExpression stmt,
            QueryExpression qs,
            JavaTypeMapping ownerMapping,
          LogicSetExpression ownerTe,
          DatastoreIdentifier collectionTableAlias,
            Class filteredElementType,
            ScalarExpression elmExpr,
            DatastoreIdentifier elementTableAlias,
            boolean existsQuery)
    {
        throw new JPOXUserException("Cannot query sets obtained by Map.entrySet()");
    }
}
TOP

Related Classes of org.jpox.store.rdbms.scostore.MapEntrySetStore

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.