Package org.jpox.store.rdbms.scostore

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

/**********************************************************************
Copyright (c) 2003 David Jencks 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
2003 Andy Jefferson - updated to support inherited objects
2003 Andy Jefferson - revised logging
2004 Andy Jefferson - merged IteratorStmt and GetStmt into GetRangeStmt
2005 Andy Jefferson - added embedded PC element capability
2005 Andy Jefferson - added dependent-element when removed from collection
    ...
**********************************************************************/
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.Collections;
import java.util.Iterator;
import java.util.ListIterator;

import org.jpox.ClassLoaderResolver;
import org.jpox.ManagedConnection;
import org.jpox.ObjectManager;
import org.jpox.StateManager;
import org.jpox.Transaction;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXException;
import org.jpox.metadata.CollectionMetaData;
import org.jpox.store.StoreManager;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.mapping.EmbeddedElementPCMapping;
import org.jpox.store.mapped.mapping.ReferenceMapping;
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.scostore.ListStore;

/**
* Abstract representation of a backing store for a List.
*
* @version $Revision: 1.94 $
**/
abstract class AbstractListStore extends AbstractCollectionStore implements ListStore
{
    protected String listName;

    protected String removeAtStmt;
    protected String setStmt;
    protected String shiftStmt;
    protected String indexOfStmt;
    protected String lastIndexOfStmt;

    /** Whether the list is indexed (like with JDO). If false then it will have no orderMapping (like with JPA). */
    protected boolean indexedList = true;

    /**
     * Constructor. Protected to prevent instantiation.
     * @param storeMgr Manager for the store
     * @param clr ClassLoader resolver
     **/
    protected AbstractListStore(StoreManager storeMgr, ClassLoaderResolver clr)
    {
        super(storeMgr, clr);
    }

    // -------------------------- List Method implementations ------------------

    /**
     * Accessor for an iterator through the list elements.
     * @param sm State Manager for the container.
     * @return The Iterator
     **/
    public Iterator iterator(StateManager sm)
    {
        return listIterator(sm);
    }

    /**
     * Accessor for an iterator through the list elements.
     * @param sm State Manager for the container.
     * @return The List Iterator
     **/
    public ListIterator listIterator(StateManager sm)
    {
        return listIterator(sm, 0);
    }

    /**
     * Accessor for an iterator through the list elements.
     * @param ownerSM State Manager for the container.
     * @param start The start point in the list.
     * @return The List Iterator
     **/
    public ListIterator listIterator(StateManager ownerSM, int start)
    {
        // Create the basic statement (without any selected columns)
        QueryExpression stmt = null;
        if (start >= 0)
        {
            stmt = getIteratorStatement(ownerSM, start, -1);
        }
        else
        {
            stmt = getIteratorStatement(ownerSM, -1, -1);
        }

        // Add the required field selections to the statement
        ResultObjectFactory rof = newResultObjectFactory(ownerSM, stmt, false, true);

        ObjectManager om = ownerSM.getObjectManager();
        Transaction tx = om.getTransaction();
        boolean useUpdateLock = ((Boolean)tx.getOptions().get("transaction.serializeReadObjects")).booleanValue();
        String statement = storeMgr.getStatementTextForQuery(stmt, useUpdateLock);

        ListIterator iter;
        try
        {
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();

            try
            {
                PreparedStatement ps = storeMgr.getStatementForQuery(stmt, om, mconn, useUpdateLock, null, null);
                try
                {
                    ResultSet rs = sqlControl.executeStatementQuery(mconn, statement, ps);
                    try
                    {
                        iter = new ListStoreIterator(ownerSM, rs, rof);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056006", statement), e);
        }

        return iter;
    }

    /**
     * Inner class providing iterator.
     */
    private class ListStoreIterator implements ListIterator
    {
        private final StateManager sm;
        private final ObjectManager om;
        private final ListIterator delegate;
        private Object lastElement = null;
        private int currentIndex = -1;

        /**
         * Constructor
         * @param sm the StateManager
         * @param rs the ResultSet
         * @param rof the Query.ResultObjectFactory
         * @throws SQLException
         */
        public ListStoreIterator(StateManager sm, ResultSet rs, ResultObjectFactory rof)
        throws SQLException
        {
            this.sm = sm;
            this.om = sm.getObjectManager();

            ArrayList results = new ArrayList();
            if (rs != null)
            {
                while (rs.next())
                {
                    Object nextElement;
                    if (elementsAreEmbedded || elementsAreSerialised)
                    {
                        int param[] = new int[elementMapping.getNumberOfDatastoreFields()];
                        for (int i = 0; i < param.length; ++i)
                        {
                            param[i] = i + 1;
                        }

                        if (elementMapping instanceof SerialisedPCMapping ||
                            elementMapping instanceof SerialisedReferenceMapping ||
                            elementMapping instanceof EmbeddedElementPCMapping)
                        {
                            // Element = Serialised
                            int ownerFieldNumber = -1;
                            if (containerTable != null)
                            {
                                ownerFieldNumber = ((JoinTable)containerTable).getOwnerFieldMetaData().getAbsoluteFieldNumber();
                            }
                            nextElement = elementMapping.getObject(om, rs, param, sm, ownerFieldNumber);
                        }
                        else
                        {
                            // Element = Non-PC
                            nextElement = elementMapping.getObject(om, rs, param);
                        }
                    }
                    else if (elementMapping instanceof ReferenceMapping)
                    {
                        // Element = Reference (Interface/Object)
                        int param[] = new int[elementMapping.getNumberOfDatastoreFields()];
                        for (int i = 0; i < param.length; ++i)
                        {
                            param[i] = i + 1;
                        }
                        nextElement = elementMapping.getObject(om, rs, param);
                    }
                    else
                    {
                        nextElement = rof.getObject(om, rs);
                    }

                    results.add(nextElement);
                }
            }
            delegate = results.listIterator();
        }

        public void add(Object o)
        {
            currentIndex = delegate.nextIndex();
            AbstractListStore.this.add(sm, o, currentIndex, -1);
            delegate.add(o);
            lastElement = null;
        }

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

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

        public Object next()
        {
            currentIndex = delegate.nextIndex();
            lastElement = delegate.next();

            return lastElement;
        }

        public int nextIndex()
        {
            return delegate.nextIndex();
        }

        public Object previous()
        {
            currentIndex = delegate.previousIndex();
            lastElement = delegate.previous();

            return lastElement;
        }

        public int previousIndex()
        {
            return delegate.previousIndex();
        }

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

            AbstractListStore.this.remove(sm, currentIndex, -1);
            delegate.remove();

            lastElement = null;
            currentIndex = -1;
        }

        public synchronized void set(Object o)
        {
            if (lastElement == null)
            {
                throw new IllegalStateException("No entry to replace");
            }

            AbstractListStore.this.set(sm, currentIndex, o, true);
            delegate.set(o);

            lastElement = o;
        }
    }

    /**
     * Method to add an element to the List.
     * @param sm The state manager
     * @param element The element to remove
     * @param size Size of the current list (if known, -1 if not)
     * @return Whether it was added successfully.
     */
    public boolean add(StateManager sm, Object element, int size)
    {
        return internalAdd(sm, 0, true, Collections.singleton(element), size);
    }

    /**
     * Method to add an element to the List.
     * @param element The element to add.
     * @param index The location to add at
     * @param sm The state manager.
     */
    public void add(StateManager sm, Object element, int index, int size)
    {
        internalAdd(sm, index, false, Collections.singleton(element), size);
    }

    /**
     * Method to add a collection of elements to the List.
     * @param sm The state manager
     * @param elements The elements to remove
     * @param size Current size of the list (if known). -1 if not known
     * @return Whether they were added successfully.
     */
    public boolean addAll(StateManager sm, Collection elements, int size)
    {
        return internalAdd(sm, 0, true, elements, size);
    }

    /**
     * Method to add all elements from a Collection to the List.
     * @param sm The state manager
     * @param elements The collection
     * @param index The location to add at
     * @param size Current size of the list (if known). -1 if not known
     * @return Whether it was successful
     */
    public boolean addAll(StateManager sm, Collection elements, int index, int size)
    {
        return internalAdd(sm, index, false, elements, size);
    }

    /**
     * Internal method for adding an item to the List.
     * @param sm The state manager
     * @param startAt The start position
     * @param atEnd Whether to add at the end
     * @param elements The Collection of elements to add.
     * @param size Current size of List (if known). -1 if not known
     * @return Whether it was successful
     */
    protected abstract boolean internalAdd(StateManager sm, int startAt, boolean atEnd, Collection elements, int size);

    /**
     * Method to retrieve an element from the List.
     * @param sm The state manager
     * @param index The location of the element.
     * @return The object
     */
    public Object get(StateManager sm, int index)
    {
        java.util.List list = internalGetRange(sm, index, index);
        if (list == null || list.size() == 0)
        {
            return null;
        }

        return list.get(0);
    }

    /**
     * Accessor for the indexOf an object in the List.
     * @param sm The state manager
     * @param element The element.
     * @return The index
     **/
    public int indexOf(StateManager sm, Object element)
    {
        return internalIndexOf(sm, element, getIndexOfStmt());
    }

    /**
     * Method to retrieve the last index of an object in the list.
     * @param sm The state manager.
     * @param element The object
     * @return The last index
     **/
    public int lastIndexOf(StateManager sm, Object element)
    {
        return internalIndexOf(sm, element, getLastIndexOfStmt());
    }

    /**
     * Remove all elements from a collection from the association owner vs
     * elements.
     * TODO : Change the query to do all in one go for efficiency. Currently
     * removes an element and shuffles the indexes, then removes an element
     * and shuffles the indexes, then removes an element and shuffles the
     * indexes etc ... a bit inefficient !!!
     * @param sm State Manager for the container
     * @param elements Collection of elements to remove
     * @return Whether the database was updated
     */
    public boolean removeAll(StateManager sm, Collection elements, int size)
    {
        if (elements == null || elements.size() == 0)
        {
            return false;
        }

        boolean modified = false;
        if (indexedList)
        {
            // Get the indices of the elements to remove in reverse order (highest first)
            int[] indices = getIndicesOf(sm,elements);

            // Remove each element in turn, doing the shifting of indexes each time
            // TODO : Change this to remove all in one go and then shift once
            for (int i=0;i<indices.length;i++)
            {
                removeAt(sm, indices[i], -1);
                modified = true;
            }
        }
        else
        {
            // Ordered List
            // TODO Remove the list item
            throw new JPOXException("Not yet implemented AbstractListStore.remove for ordered lists");
        }

        if (ownerMemberMetaData.getCollection().isDependentElement())
        {
            // "delete-dependent" : delete elements if the collection is marked as dependent
            // TODO What if the collection contains elements that are not in the List ? should not delete them
            sm.getObjectManager().deleteObjects(elements.toArray());
        }

        return modified;
    }

    /**
     * Method to remove the specified element from the List.
     * @param sm The state manager
     * @param element The element to remove.
     * @param size Current size of list if known. -1 if not known
     * @param allowDependentField Whether to allow any cascade deletes caused by this removal
     * @return Whether it was removed successfully.
     */
    public boolean remove(StateManager sm, Object element, int size, boolean allowDependentField)
    {
        if (!validateElementForReading(sm, element))
        {
            return false;
        }

        boolean modified = internalRemove(sm, element, size);

        CollectionMetaData collmd = ownerMemberMetaData.getCollection();
        if (allowDependentField && collmd.isDependentElement() && !collmd.isEmbeddedElement())
        {
            // Delete the element if it is dependent
            sm.getObjectManager().deleteObjectInternal(element);
        }

        return modified;
    }

    /**
     * Convenience method to remove the specified element from the List.
     * @param sm StateManager of the owner
     * @param element The element
     * @param size Current size of list if known. -1 if not known
     * @return Whether the List was modified
     */
    protected abstract boolean internalRemove(StateManager sm, Object element, int size);

    /**
     * Method to remove an object at an index in the List.
     * @param index The location
     * @param sm The state manager
     * @param size Current size of the list (if known). -1 if not known
     * @return The object that was removed
     */
    public Object remove(StateManager sm, int index, int size)
    {
        Object element = get(sm, index);
        removeAt(sm, index, size);

        CollectionMetaData collmd = ownerMemberMetaData.getCollection();
        if (collmd.isDependentElement() && !collmd.isEmbeddedElement())
        {
            if (!contains(sm, element))
            {
                // Delete the element if it is dependent and doesnt have a duplicate entry in the list
                sm.getObjectManager().deleteObjectInternal(element);
            }
        }

        return element;
    }

    /**
     * Internal method to remove an object at a location from the List.
     * @param sm The state manager
     * @param index The location
     * @param size Current list size (if known). -1 if not known
     */
    protected abstract void removeAt(StateManager sm, int index, int size);

    /**
     * Method to retrieve a list of elements in a range.
     * @param sm The state manager.
     * @param start From index (inclusive)
     * @param end To index (exclusive)
     * @return Sub List of elements in this range.
     **/
    public java.util.List subList(StateManager sm, int start, int end)
    {
        return internalGetRange(sm,start,end);
    }

    // -------------------------- Common Statements ----------------------------

    /**
     * Generate statement for getting the index of an item.
     * <PRE>
     * SELECT INDEXCOL FROM LISTTABLE
     * WHERE OWNERCOL=?
     * AND ELEMENTCOL=?
     * [AND EMBEDDEDFIELD1=? AND EMBEDDEDFIELD2=? AND ...]
     * [AND DISTINGUISHER=?]
     * ORDER BY INDEXCOL
     * </PRE>
     * @return The Statement for getting the index of an item
     */
    protected String getIndexOfStmt()
    {
        if (indexOfStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("SELECT ");
            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(",");
                }
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            }
            stmt.append(" FROM ");
            stmt.append(containerTable.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());
            }

            for (int i=0; i<elementMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(" AND ");
                stmt.append(elementMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping)elementMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(i)).getUpdateInputParameter());
                }
            }

            stmt.append(" ORDER BY ");
            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(",");
                }
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            }
            indexOfStmt = stmt.toString();
        }
        return indexOfStmt;
    }

    /**
     * Generates the statement for getting the index of the last item.
     * <PRE>
     * SELECT INDEXCOL FROM LISTTABLE
     * WHERE OWNERCOL=?
     * AND ELEMENTCOL=?
     * [AND EMBEDDEDFIELD1=? AND EMBEDDEDFIELD2=? AND ...]
     * [AND DISTINGUISHER=?]
     * ORDER BY INDEXCOL DESC
     * </PRE>
     * @return The Statement for getting the last item
     */
    protected String getLastIndexOfStmt()
    {
        if (lastIndexOfStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("SELECT ");
            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(",");
                }
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            }
            stmt.append(" FROM ");
            stmt.append(containerTable.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());
            }

            for (int i=0; i<elementMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(" AND ");
                stmt.append(elementMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping)elementMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(i)).getUpdateInputParameter());
                }
            }

            stmt.append(" ORDER BY ");
            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(",");
                }
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" DESC ");
            }
            lastIndexOfStmt = stmt.toString();
        }
        return lastIndexOfStmt;
    }

    /**
     * Generates the statement for removing an item.
     * <PRE>
     * DELETE FROM LISTTABLE
     * WHERE OWNERCOL = ?
     * AND INDEXCOL = ?
     * [AND DISTINGUISHER=?]
     * </PRE>
     * @return The Statement for removing an item from a position
     */
    protected String getRemoveAtStmt()
    {
        if (removeAtStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("DELETE FROM ");
            stmt.append(containerTable.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());
            }

            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(" AND ");
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping)orderMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(i)).getUpdateInputParameter());
                }
            }
            removeAtStmt = stmt.toString();
        }
        return removeAtStmt;
    }

    /**
     * Generates the statement for shifting items.
     * <PRE>
     * UPDATE LISTTABLE SET INDEXCOL = ?
     * WHERE OWNERCOL = ?
     * AND INDEXCOL = ?
     * [AND DISTINGUISHER=?]
     * </PRE>
     * @return The Statement for shifting elements
     */
    protected String getShiftStmt()
    {
        if (shiftStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("UPDATE ");
            stmt.append(containerTable.toString());
            stmt.append(" SET ");

            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(",");
                }
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping)orderMapping.getDataStoreMapping(i)).getUpdateInputParameter());
                stmt.append(" + ");
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().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());
            }

            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(" AND ");
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping)orderMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(i)).getUpdateInputParameter());
                }
            }
            shiftStmt = stmt.toString();
        }
        return shiftStmt;
    }

    /**
     * Generates the statement for getting the indices of a collection of
     * element. Order into descending index order (highest first) so they will
     * NOT be in the same order as they appear in the input collection "elements".
     * <PRE>
     * SELECT INDEXCOL FROM LISTTABLE
     * WHERE (OWNERCOL=? AND ELEMENT_COL=? [AND DISTINGUISHER=?]) OR
     *       (OWNERCOL=? AND ELEMENT_COL=? [AND DISTINGUISHER=?]) OR
     *       (OWNERCOL=? AND ELEMENT_COL=? [AND DISTINGUISHER=?])
     * ORDER BY INDEXCOL DESC
     * </PRE>
     * @param elements The elements to retrieve the indices for.
     * @return The Statement for getting the indices of the collection.
     */
    protected String getIndicesOfStmt(Collection elements)
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("SELECT ");
        for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
        {
            if (i > 0)
            {
                stmt.append(",");
            }
            stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
        }
        stmt.append(" FROM ");
        stmt.append(containerTable.toString());
        stmt.append(" WHERE ");
        Iterator iter=elements.iterator();
        boolean first_element=true;
        while (iter.hasNext())
        {
            iter.next(); // Move to next element

            if (!first_element)
            {
                stmt.append(" OR (");
            }
            else
            {
                stmt.append("(");
            }

            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());
            }

            for (int i=0; i<elementMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(" AND ");
                stmt.append(elementMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping)elementMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(i)).getUpdateInputParameter());
                }
            }

            stmt.append(")");
            first_element = false;
        }

        stmt.append(" ORDER BY ");
        for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
        {
            if (i > 0)
            {
                stmt.append(",");
            }
            stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append(" DESC");
        }

        return stmt.toString();
    }

    /**
     * Method to prepare the indicesOf statement for use. Populates the various
     * parameters. This is required because the query is built dynamically
     * depending on the number of elements to retrieve the indices for.
     * @param sm State Manager of the container.
     * @param ps The Prepared Statement
     * @param elements Collection of elements
     **/
    protected void prepareIndicesOfStmt(StateManager sm,
                                        PreparedStatement ps,
                                        Collection elements)
    {
        if (elements == null || elements.size() == 0)
        {
            return;
        }

        ObjectManager om = sm.getObjectManager();
        Iterator iter=elements.iterator();
        int jdbcPosition = 1;
        while (iter.hasNext())
        {
            Object element = iter.next();

            jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
            jdbcPosition = populateElementInStatement(om, ps, element, jdbcPosition);
            if (relationDiscriminatorMapping != null)
            {
                jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
            }
        }
    }

    /**
     * Internal method to remove an object at a location in the List.
     * @param sm The state manager.
     * @param index The location
     * @param stmt The statement to remove the element from the List
     * @param size Current list size (if known). -1 if not known
     */
    protected void internalRemoveAt(StateManager sm, int index, String stmt, int size)
    {
        int currentListSize = 0;
        if (size < 0)
        {
            // Get the current size from the datastore
            currentListSize = size(sm);
        }
        else
        {
            currentListSize = size;
        }

        ObjectManager om = sm.getObjectManager();
        try
        {
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, stmt, false);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                    jdbcPosition = populateOrderInStatement(om, ps, index, jdbcPosition);
                    if (relationDiscriminatorMapping != null)
                    {
                        jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
                    }

                    int[] rowsDeleted = sqlControl.executeStatementUpdate(mconn, stmt, ps, true);
                    if (rowsDeleted[0] == 0)
                    {
                        //?? throw exception??
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }

                // shift down
                if (index != currentListSize - 1)
                {
                    for (int i=index + 1; i<currentListSize; i++)
                    {
                        // Shift this index down 1
                        internalShift(sm, mconn, false, i, -1, true);
                    }
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056012",stmt),e);
        }
    }

    /**
     * Method to process a "shift" statement, updating the index in the list of the specified index.
     * @param ownerSM StateManager of the owner
     * @param conn The connection
     * @param batched Whether the statement is batched
     * @param oldIndex The old index
     * @param amount Amount to shift by (negative means shift down)
     * @param executeNow Whether to execute the statement now (or wait for batching)
     * @return Return code(s) from any executed statements
     * @throws SQLException Thrown if an error occurs
     */
    protected int[] internalShift(StateManager ownerSM, ManagedConnection conn, boolean batched, int oldIndex, int amount, boolean executeNow)
    throws SQLException
    {
        ObjectManager om = ownerSM.getObjectManager();
        SQLController sqlControl = storeMgr.getSQLController();
        String shiftStmt = getShiftStmt();
        PreparedStatement ps = sqlControl.getStatementForUpdate(conn, shiftStmt, false);
        try
        {
            int jdbcPosition = 1;
            jdbcPosition = populateOrderInStatement(om, ps, amount, jdbcPosition);
            jdbcPosition = populateOwnerInStatement(ownerSM, om, ps, jdbcPosition);
            jdbcPosition = populateOrderInStatement(om, ps, oldIndex, jdbcPosition);
            if (relationDiscriminatorMapping != null)
            {
                jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
            }

            // Execute the statement
            return sqlControl.executeStatementUpdate(conn, shiftStmt, ps, executeNow);
        }
        finally
        {
            sqlControl.closeStatement(conn, ps);
        }
    }

    /**
     * Internal method to find the index of an element.
     * @param sm The state manager.
     * @param element The element
     * @param stmt The statement to find the element.
     * @return The index of the element in the List.
     **/
    private int internalIndexOf(StateManager sm, Object element, String stmt)
    {
        validateElementForReading(sm, element);

        try
        {
            ObjectManager om = sm.getObjectManager();
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, stmt, false);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                    jdbcPosition = populateElementInStatement(om, ps, element, jdbcPosition);
                    if (relationDiscriminatorMapping != null)
                    {
                        jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
                    }

                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        boolean found = rs.next();
                        if (!found)
                        {
                            SQLWarnings.log(rs);
                            return -1;
                        }
                        int index = rs.getInt(1);
                        SQLWarnings.log(rs);
                        return index;
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056017",stmt),e);
        }
    }

    /**
     * Utility to find the indices of a collection of elements.
     * The returned list are in reverse order (highest index first).
     * @param sm The state manager.
     * @param elements The elements
     * @return The indices of the elements in the List.
     **/
    protected int[] getIndicesOf(StateManager sm, Collection elements)
    {
        if (elements == null || elements.size() == 0)
        {
            return null;
        }

        Iterator iter=elements.iterator();
        while (iter.hasNext())
        {
            Object element=iter.next();
            validateElementForReading(sm, element);
        }

        String stmt = getIndicesOfStmt(elements);
        int[] indices = new int[elements.size()];
        try
        {
            ObjectManager om = sm.getObjectManager();
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, stmt, false);
                try
                {
                    prepareIndicesOfStmt(sm,ps,elements);

                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        int i=0;
                        while (rs.next())
                        {
                            indices[i++] = rs.getInt(1);
                        }

                        if (i < elements.size())
                        {
                            throw new JPOXDataStoreException(LOCALISER.msg("056023", stmt));
                        }
                        SQLWarnings.log(rs);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("RDBMS.SCO.IndicesofRequestFailed",stmt),e);
        }

        return indices;
    }

    /**
     * Accessor for the iterator statement. This has 5 modes
     * <UL>
     * <LI>start and end have the same value and &ge;0 then get that element</LI>
     * <LI>start and end have values &ge;0 and are different then get a range</LI>
     * <LI>start is &ge;0 and end is -1 the get all from start (inclusive).</LI>
     * <LI>start is -1 and end is &ge;0 the get all up to end(exclusive).</LI>
     * <LI>start and end values are both -1 then get all elements</LI>
     * </UL>
     * @param ownerSM The StateManager
     * @param startIndex The start position in the List.
     * @param endIndex The end position in the List.
     * @return The QueryStatement to iterate through the required elements.
     **/
    protected abstract QueryExpression getIteratorStatement(StateManager ownerSM, int startIndex, int endIndex);

    /**
     * Utility to retrieve (as a List) the elements for the List in a range
     * depending on the specified start/end positions.
     * @param sm State Manager for the container
     * @param start Start position in the List.
     * @param end End position in the List.
     * @return The List of elements matching the range specification.
     **/
    protected java.util.List internalGetRange(StateManager sm,int start,int end)
    {
        java.util.List list = new java.util.ArrayList();

        ObjectManager om = sm.getObjectManager();
        QueryExpression stmt = getIteratorStatement(sm, start, end);
        ResultObjectFactory getROF = newResultObjectFactory(sm,stmt,false,true);
        Transaction tx = om.getTransaction();
        boolean useUpdateLock = ((Boolean)tx.getOptions().get("transaction.serializeReadObjects")).booleanValue();
        String statement = storeMgr.getStatementTextForQuery(stmt, useUpdateLock);

        try
        {
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = storeMgr.getStatementForQuery(stmt, om, mconn, useUpdateLock, null, null);
                try
                {
                    ResultSet rs = sqlControl.executeStatementQuery(mconn, statement, ps);
                    try
                    {
                        while (rs.next())
                        {
                            Object obj = null;

                            if (elementsAreEmbedded || elementsAreSerialised)
                            {
                                int param[] = new int[elementMapping.getNumberOfDatastoreFields()];
                                for (int i = 0; i < param.length; ++i)
                                {
                                    param[i] = i + 1;
                                }
                                obj = elementMapping.getObject(om, rs, param);
                            }
                            else if (elementMapping instanceof ReferenceMapping)
                            {
                                // Element = Reference (Interface/Object)
                                int param[] = new int[elementMapping.getNumberOfDatastoreFields()];
                                for (int i = 0; i < param.length; ++i)
                                {
                                    param[i] = i + 1;
                                }
                                obj = elementMapping.getObject(om, rs, param);
                            }
                            else
                            {
                                obj = getROF.getObject(om, rs);
                            }

                            list.add(obj);
                        }

                        SQLWarnings.log(rs);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056014",statement),e);
        }

        return list;
    }
}
TOP

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

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.