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 }