001    package org.apache.ojb.broker.accesslayer;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.ArrayList;
019    import java.util.Iterator;
020    import java.util.List;
021    
022    import org.apache.ojb.broker.PersistenceBrokerException;
023    
024    /**
025     * @author matthew.baird (mattbaird@yahoo.com)
026     *
027     * The ChainingIterator is an extent aware Iterator.
028     *
029     * How the ChainingIterator works:
030     * The ChainedIterator holds a collection of RsIterators for each queried 
031     * Interface-based extent.
032     *
033     * The RsIterator is able to load objects that are non-interface extents, 
034     * mapped to the same table.
035     *
036     * The ChainingIterator cannot return sorted results as the iterator is a 
037     * collection of query results across different tables.
038     *
039     * @version $Id: ChainingIterator.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
040     */
041    public class ChainingIterator implements OJBIterator
042    {
043        private List m_rsIterators = new ArrayList();
044        private OJBIterator m_activeIterator = null;
045    
046        /**
047         * The following are used to maintain an index of where
048         * the cursor is in the array of rsiterators. We do this
049         * because we can't find the position through the interface,
050         * and we need the position in order to support the relative(x)
051         * calls
052         */
053        private int m_activeIteratorIndex = 0;
054        private int m_fullSize = -1;
055        private int m_currentCursorPosition = 0;
056        /** if true do not fire PBLifeCycleEvent. */
057        private boolean disableLifeCycleEvents = false;
058    
059        /**
060         * Constructor for ChainingIterator.
061         */
062        public ChainingIterator()
063        {
064            super();
065        }
066    
067        /**
068         * Constructor for ChainingIterator.
069         */
070        public ChainingIterator(List iterators)
071        {
072            Iterator checkIterator = iterators.iterator();
073            OJBIterator temp;
074    
075            /**
076             * validate that all items in List are iterators and
077             * they are not empty.
078             */
079            while (checkIterator.hasNext())
080            {
081                temp = (OJBIterator) checkIterator.next();
082                addIterator(temp);
083            }
084        }
085    
086        /**
087         * use this method to construct the ChainingIterator
088         * iterator by iterator.
089         */
090        public void addIterator(OJBIterator iterator)
091        {
092            /**
093             * only add iterators that are not null and non-empty.
094             */
095            if (iterator != null)
096            {
097                if (iterator.hasNext())
098                {
099                    setNextIterator();
100                    m_rsIterators.add(iterator);
101                }
102            }
103        }
104    
105        /**
106         * Calculates the size of all the iterators. Caches it for fast
107         * lookups in the future. iterators shouldn't change size after the
108         * queries have been executed so caching is safe (assumption, should check).
109         * @return the combined size of all the iterators for all extents.
110         */
111        public int size() throws PersistenceBrokerException
112        {
113            if (m_fullSize == -1)
114            {
115                int size = 0;
116                Iterator it = m_rsIterators.iterator();
117                while (it.hasNext())
118                {
119                    size += ((OJBIterator) it.next()).size();
120                }
121                m_fullSize = size;
122            }
123            return m_fullSize;
124        }
125    
126        /* (non-Javadoc)
127         * @see org.apache.ojb.broker.accesslayer.OJBIterator#fullSize()
128         */
129        public int fullSize() throws PersistenceBrokerException
130        {
131            return size();
132        }
133    
134        /**
135         * the absolute and relative calls are the trickiest parts. We have to
136         * move across cursor boundaries potentially.
137         * 
138         * a + row value indexes from beginning of resultset
139         * a - row value indexes from the end of th resulset.
140         * 
141         * Calling absolute(1) is the same as calling first(). 
142         * Calling absolute(-1) is the same as calling last().
143         */
144        public boolean absolute(int row) throws PersistenceBrokerException
145        {
146            // 1. handle the special cases first.
147            if (row == 0)
148            {
149                return true;
150            }
151    
152            if (row == 1)
153            {
154                m_activeIteratorIndex = 0;
155                m_activeIterator = (OJBIterator) m_rsIterators.get(m_activeIteratorIndex);
156                m_activeIterator.absolute(1);
157                return true;
158            }
159            if (row == -1)
160            {
161                m_activeIteratorIndex = m_rsIterators.size();
162                m_activeIterator = (OJBIterator) m_rsIterators.get(m_activeIteratorIndex);
163                m_activeIterator.absolute(-1);
164                return true;
165            }
166    
167            // now do the real work.
168            boolean movedToAbsolute = false;
169            boolean retval = false;
170            setNextIterator();
171    
172            // row is positive, so index from beginning.
173            if (row > 0)
174            {
175                int sizeCount = 0;
176                Iterator it = m_rsIterators.iterator();
177                OJBIterator temp = null;
178                while (it.hasNext() && !movedToAbsolute)
179                {
180                    temp = (OJBIterator) it.next();
181                    if (temp.size() < row)
182                    {
183                        sizeCount += temp.size();
184                    }
185                    else
186                    {
187                        // move to the offset - sizecount
188                        m_currentCursorPosition = row - sizeCount;
189                        retval = temp.absolute(m_currentCursorPosition);
190                        movedToAbsolute = true;
191                    }
192                }
193    
194            }
195    
196            // row is negative, so index from end
197            else if (row < 0)
198            {
199                int sizeCount = 0;
200                OJBIterator temp = null;
201                for (int i = m_rsIterators.size(); ((i >= 0) && !movedToAbsolute); i--)
202                {
203                    temp = (OJBIterator) m_rsIterators.get(i);
204                    if (temp.size() < row)
205                    {
206                        sizeCount += temp.size();
207                    }
208                    else
209                    {
210                        // move to the offset - sizecount
211                        m_currentCursorPosition = row + sizeCount;
212                        retval = temp.absolute(m_currentCursorPosition);
213                        movedToAbsolute = true;
214                    }
215                }
216            }
217    
218            return retval;
219        }
220    
221        /**
222         * Moves the cursor a relative number of rows.
223         * Movement can go in forward (positive) or reverse (negative).
224         * 
225         * Calling relative does not "wrap" meaning if you move before first or 
226         * after last you get positioned at the first or last row.
227         * 
228         * Calling relative(0) does not change the cursor position.
229         * 
230         * Note: Calling the method relative(1) is different from calling 
231         * the method next() because is makes sense to call next() when 
232         * there is no current row, for example, when the cursor is 
233         * positioned before the first row or after the last row of 
234         * the result set.
235         */
236        public boolean relative(int row) throws PersistenceBrokerException
237        {
238            if (row == 0)
239            {
240                return true;
241            }
242    
243            boolean movedToRelative = false;
244            boolean retval = false;
245            setNextIterator();
246    
247            if (row > 0)
248            {
249                // special case checking for the iterator we're currently in
250                // (since it isn't positioned on the boundary potentially)
251                if (row > (m_activeIterator.size() - m_currentCursorPosition))
252                {
253                    // the relative position lies over the border of the
254                    // current iterator.
255    
256                    // starting position counter should be set to whatever we have left in
257                    // active iterator.
258                    int positionCounter = m_activeIterator.size() - m_currentCursorPosition;
259                    for (int i = m_activeIteratorIndex + 1; ((i < m_rsIterators.size()) && !movedToRelative); i++)
260                    {
261                        m_activeIteratorIndex = i;
262                        m_currentCursorPosition = 0;
263                        m_activeIterator = (OJBIterator) m_rsIterators.get(m_activeIteratorIndex);
264                        if (!((row - positionCounter) > m_activeIterator.size()))
265                        {
266                            // the relative position requested is within this iterator.
267                            m_currentCursorPosition = row - positionCounter;
268                            retval = m_activeIterator.relative(m_currentCursorPosition);
269                            movedToRelative = true;
270                        }
271                    }
272                }
273                else
274                {
275                    // the relative position lays within the current iterator.
276                    retval = m_activeIterator.relative(row);
277                    movedToRelative = true;
278                }
279            }
280    
281            return retval;
282        }
283    
284        /**
285         * delegate to each contained OJBIterator and release
286         * its resources.
287         */
288        public void releaseDbResources()
289        {
290            Iterator it = m_rsIterators.iterator();
291            while (it.hasNext())
292            {
293                ((OJBIterator) it.next()).releaseDbResources();
294            }
295        }
296    
297        /**
298         * check the list of iterators to see if we have a next element.
299         * @return true if one of the contained iterators past the current
300         * position has a next.
301         */
302        public boolean hasNext()
303        {
304            setNextIterator();
305            if (m_activeIterator == null)
306            {
307                return false;
308            }
309            else
310            {
311                return m_activeIterator.hasNext();
312            }
313        }
314    
315        /**
316         * first checks to make sure we aren't at the end of the list of
317         * iterators, positions the cursor appropriately, then retrieves
318         * next object in active iterator.
319         * @return the next object in the iterator.
320         */
321        public Object next()
322        {
323            setNextIterator();
324            m_currentCursorPosition++;
325            return m_activeIterator.next();
326        }
327    
328        public void remove()
329        {
330            setNextIterator();
331            m_activeIterator.remove();
332        }
333    
334        /**
335         * Convenience routine to move to the next iterator if needed.
336         * @return true if the iterator is changed, false if no changes.
337         */
338        private boolean setNextIterator()
339        {
340            boolean retval = false;
341            // first, check if the activeIterator is null, and set it.
342            if (m_activeIterator == null)
343            {
344                if (m_rsIterators.size() > 0)
345                {
346                    m_activeIteratorIndex = 0;
347                    m_currentCursorPosition = 0;
348                    m_activeIterator = (OJBIterator) m_rsIterators.get(m_activeIteratorIndex);
349                }
350            }
351            else if (!m_activeIterator.hasNext())
352            {
353                if (m_rsIterators.size() > (m_activeIteratorIndex + 1))
354                {
355                    // we still have iterators in the collection, move to the
356                    // next one, increment the counter, and set the active
357                    // iterator.
358                    m_activeIteratorIndex++;
359                    m_currentCursorPosition = 0;
360                    m_activeIterator = (OJBIterator) m_rsIterators.get(m_activeIteratorIndex);
361                    retval = true;
362                }
363            }
364    
365            return retval;
366        }
367    
368        /**
369         * Answer true if an Iterator for a Table is already available
370         * @param aTable
371         * @return
372         */
373        public boolean containsIteratorForTable(String aTable)
374        {
375            boolean result = false;
376    
377            if (m_rsIterators != null)
378            {
379                for (int i = 0; i < m_rsIterators.size(); i++)
380                {
381                    OJBIterator it = (OJBIterator) m_rsIterators.get(i);
382                    if (it instanceof RsIterator)
383                    {
384                        if (((RsIterator) it).getClassDescriptor().getFullTableName().equals(aTable))
385                        {
386                            result = true;
387                            break;
388                        }
389                    }
390                    else if (it instanceof ChainingIterator)
391                    {
392                        result = ((ChainingIterator) it).containsIteratorForTable(aTable);
393                    }
394                }
395            }
396    
397            return result;
398        }
399    
400        /**
401         * @see org.apache.ojb.broker.accesslayer.OJBIterator#disableLifeCycleEvents()
402         */
403        public void disableLifeCycleEvents()
404        {
405            Iterator iterators = m_rsIterators.iterator();
406            while (iterators.hasNext())
407            {
408                OJBIterator iter = (OJBIterator) iterators.next();
409                iter.disableLifeCycleEvents();
410            }        
411        }
412    
413    }