Package org.jpox.store.rdbms.query

Source Code of org.jpox.store.rdbms.query.ForwardQueryResult

/**********************************************************************
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 Erik Bengtson - fixed bug [833915] implements interface Queryable.
2003 Andy Jefferson - coding standards
2004 Andy Jefferson - added resultClass
2008 Andy Jefferson - Removed optimistic restriction
    ...
**********************************************************************/
package org.jpox.store.rdbms.query;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import javax.jdo.JDODataStoreException;
import javax.jdo.JDOUserException;

import org.jpox.FetchPlan;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.query.Query;
import org.jpox.store.query.QueryResult;
import org.jpox.store.query.ResultObjectFactory;
import org.jpox.store.rdbms.SQLWarnings;
import org.jpox.util.JPOXLogger;
import org.jpox.util.StringUtils;

/**
* Lazy collection results from a Query with the ResultSet in a forwards direction.
* In general the actual result elements are only loaded when accessed
* with the exception of non-transactional or optimistic contexts that
* load the elements at initialisation.
*
* @version $Revision: 1.10 $
*/
public final class ForwardQueryResult extends AbstractRDBMSQueryResult
    implements QueryResult, java.io.Serializable
{
    /** Whether there are still more rows to be processed in the ResultSet. */
    protected boolean moreResultSetRows;

    /** The Result Objects. */
    protected List resultObjs = new ArrayList();

    /** The candidate list */
    private Collection candidates;

    /** Whether to load any unread results at commit (when connection is closed). */
    private boolean loadResultsAtCommit = true; // Default to load

    /**
     * Constructor of the result from a Query.
     * @param qs The Query Statement
     * @param query The Query
     * @param rof The factory to retrieve results from
     * @param rs The ResultSet from the Query Statement
     * @param candidates Candidate elements. Pass this argument only when distinct = false
     * @throws SQLException if cant read ResultSet
     */
    public ForwardQueryResult(QueryExpression qs,
                       Query query,
                       ResultObjectFactory rof,
                       ResultSet rs,
                       Collection candidates)
    throws SQLException
    {
        super(qs, query, rof, rs);

        if (candidates != null)
        {
            this.candidates = new ArrayList(candidates);
        }

        // Process any supported extensions
        String ext = (String)query.getExtension("org.jpox.query.loadResultsAtCommit");
        if (ext != null)
        {
            loadResultsAtCommit = new Boolean(ext).booleanValue();
        }

        int fetchSize = query.getFetchPlan().getFetchSize();
        if (!(moreResultSetRows = rs.next()))
        {
            // ResultSet is empty, so just close it
            closeResults();
        }
        else if (!query.getObjectManager().getTransaction().isActive() || fetchSize == FetchPlan.FETCH_SIZE_GREEDY)
        {
            // No transaction or in "greedy" mode so load all results now
            advanceToEndOfResultSet();
        }
        else if (fetchSize > 0)
        {
            Iterator it = iterator();
            for (int i=0;i<fetchSize;i++)
            {
                if (it.hasNext())
                {
                    it.next();
                }
            }
        }
    }

    /**
     * Internal method to advance to the end of the ResultSet, populating
     * the resultObjs, and close the ResultSet when complete.
     */
    private void advanceToEndOfResultSet()
    {
        // Advance through any remaining result set rows
        Iterator it = iterator();
        while (it.hasNext())
        {
            it.next();
        }
    }

    /**
     * Accessor for the next object from the ResultSet.
     * @return The next element from the ResultSet.
     */
    protected Object nextResultSetElement()
    {
        if (rof == null)
        {
            // Already disconnected
            return null;
        }

        Object nextElement = rof.getObject(query.getObjectManager(), rs);
        SQLWarnings.log(rs);
        resultObjs.add(nextElement);

        try
        {
            if (!(moreResultSetRows = rs.next()))
            {
                closeResults();
            }
        }
        catch (SQLException e)
        {
            if (query.getObjectManager().getOMFContext().getApi().equalsIgnoreCase("JDO"))
            {
                throw new JDODataStoreException(LOCALISER.msg("052601",e));
            }
            else
            {
                throw new JPOXDataStoreException(LOCALISER.msg("052601",e));
            }
        }

        return nextElement;
    }

    /**
     * Method to close the results, making the results unusable thereafter.
     */
    public synchronized void close()
    {
        moreResultSetRows = false;
        resultObjs.clear();

        super.close();
    }

    /**
     * Method called to inform the query result that the connection is being closed so perform
     * any required operations now, or rest in peace.
     */
    protected void closingConnection()
    {
        if (loadResultsAtCommit && isOpen() && moreResultSetRows)
        {
            // Query connection closing message
            JPOXLogger.QUERY.info(LOCALISER.msg("052606", query.toString()));

            try
            {
                // If we are still open navigate to the end of the ResultSet before it gets closed
                advanceToEndOfResultSet();
            }
            catch (JPOXUserException jpue)
            {
                // TODO Localise this message
                // Log any exception - can get exceptions when maybe the user has specified an invalid result class etc
                JPOXLogger.QUERY.warn("Exception thrown while loading remaining rows of query : " + jpue.getMessage());
            }
            catch (JDOUserException ue)
            {
                // Log any exception - can get exceptions when maybe the user has specified an invalid result class etc
                JPOXLogger.QUERY.warn("Exception thrown while loading remaining rows of query : " + ue.getMessage());
            }
        }
    }

    // ---------------------- Implementation of List methods -------------------------

    /**
     * Accessor for an iterator for the results.
     * @return The iterator
     */
    public Iterator iterator()
    {
        return new QueryResultIterator();
    }

    /**
     * Accessor for an iterator for the results.
     * @return The iterator
     */
    public ListIterator listIterator()
    {
        return new QueryResultIterator();
    }

    /**
     * An Iterator results of a pm.query.execute().iterator()
     */
    private class QueryResultIterator implements ListIterator
    {
        private int nextRowNum = 0;

        /** hold the last element **/
        Object nextElement = null;
       
        /**
         * Constructor
         */
        public QueryResultIterator()
        {
        }

        public void add(Object obj)
        {
            throw new UnsupportedOperationException(LOCALISER.msg("052603"));
        }

        public boolean hasNext()
        {
            synchronized (ForwardQueryResult.this)
            {
                if (!isOpen())
                {
                    // Spec 14.6.7 Calling hasNext() on closed Query will return false
                    return false;
                }
                if (nextRowNum < resultObjs.size())
                {
                    return true;
                }
                if (candidates != null && nextElement != null && !moreResultSetRows)
                {
                    return candidates.contains(nextElement);
                }
                return moreResultSetRows;
            }
        }

        public boolean hasPrevious()
        {
            // We only navigate in forward direction, but maybe could provide this method
            throw new UnsupportedOperationException("Not yet implemented");
        }

        public Object next()
        {
            synchronized (ForwardQueryResult.this)
            {
                if (!isOpen())
                {
                    // Spec 14.6.7 Calling next() on closed Query will throw NoSuchElementException
                    throw new NoSuchElementException(LOCALISER.msg("052600"));
                }
                if (candidates != null && nextElement != null)
                {
                    // Allow for candidate collections with dups, so we can return the same element multiple times
                    // This assumes that there is a single object returned of candidate type
                    if (candidates.remove(nextElement))
                    {
                        // Returning candidate type and candidates has dup elements, so return it til we exhaust candidates
                        resultObjs.add(nextElement);
                        return nextElement;
                    }
                }
                if (nextRowNum < resultObjs.size())
                {
                    nextElement = resultObjs.get(nextRowNum);
                    ++nextRowNum;
                    return nextElement;
                }
                else if (moreResultSetRows)
                {
                    nextElement = nextResultSetElement();
                    ++nextRowNum;
                    if (candidates != null)
                    {
                        // Remove from the candidates since we have processed it now
                        // This assumes that there is a single object returned of candidate type
                        candidates.remove(nextElement);
                    }

                    return nextElement;
                }
                throw new NoSuchElementException(LOCALISER.msg("052602"));
            }
        }

        public int nextIndex()
        {
            throw new UnsupportedOperationException("Not yet implemented");
        }

        public Object previous()
        {
            throw new UnsupportedOperationException("Not yet implemented");
        }

        public int previousIndex()
        {
            throw new UnsupportedOperationException("Not yet implemented");
        }

        public void remove()
        {
            throw new UnsupportedOperationException(LOCALISER.msg("052603"));
        }

        public void set(Object obj)
        {
            throw new UnsupportedOperationException(LOCALISER.msg("052603"));
        }
    }

    /**
     * Method to check if the specified object is contained in this result.
     * @param o The object
     * @return Whether it is contained here.
     */
    public synchronized boolean contains(Object o)
    {
        assertIsOpen();
        advanceToEndOfResultSet();

        return resultObjs.contains(o);
    }

    /**
     * Method to check if all of the specified objects are contained here.
     * @param c The collection of objects
     * @return Whether they are all contained here.
     */
    public synchronized boolean containsAll(Collection c)
    {
        assertIsOpen();
        advanceToEndOfResultSet();

        return resultObjs.containsAll(c);
    }

    /**
     * Equality operator for QueryResults.
     * Overrides the AbstractList implementation since that uses
     * size() and iterator() and that would cause problems when closed.
     * @param o The object to compare against
     * @return Whether they are equal
     */
    public boolean equals(Object o)
    {
        if (o == null || !(o instanceof ForwardQueryResult))
        {
            return false;
        }

        ForwardQueryResult other = (ForwardQueryResult)o;
        if (qs != null)
        {
            return other.qs == qs;
        }
        else if (query != null)
        {
            return other.query == query;
        }
        return StringUtils.toJVMIDString(other).equals(StringUtils.toJVMIDString(this));
    }

    /**
     * Method to retrieve a particular element from the list.
     * @param index The index of the element
     * @return The element at index
     */
    public synchronized Object get(int index)
    {
        assertIsOpen();
        advanceToEndOfResultSet();

        return resultObjs.get(index);
    }

    /**
     * Returns <tt>true</tt> if this collection contains no elements.<p>
     * This implementation check if the result is empty.
     *
     * @return <tt>true</tt> if this collection contains no elements.
     */
    public synchronized boolean isEmpty()
    {
        assertIsOpen();

        return resultObjs.isEmpty() && !moreResultSetRows;
    }

    /**
     * Method to return the size of the result.
     * @return The size of the result.
     */
    public synchronized int size()
    {
        assertIsOpen();
        advanceToEndOfResultSet();

        return resultObjs.size();
    }

    /**
     * Method to return the results as an array.
     * @return The array.
     */
    public synchronized Object[] toArray()
    {
        assertIsOpen();
        advanceToEndOfResultSet();

        return resultObjs.toArray();
    }

    /**
     * Method to return the results as an array.
     * @param a The array to copy into.
     * @return The array.
     */
    public synchronized Object[] toArray(Object[] a)
    {
        assertIsOpen();
        advanceToEndOfResultSet();

        return resultObjs.toArray(a);
    }
}
TOP

Related Classes of org.jpox.store.rdbms.query.ForwardQueryResult

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.
Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.