KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > collections > StoredIterator


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2000,2006 Oracle. All rights reserved.
5  *
6  * $Id: StoredIterator.java,v 1.45 2006/10/30 21:14:10 bostic Exp $
7  */

8
9 package com.sleepycat.collections;
10
11 import java.util.Iterator JavaDoc;
12 import java.util.ListIterator JavaDoc;
13 import java.util.NoSuchElementException JavaDoc;
14
15 import com.sleepycat.je.DatabaseException;
16 import com.sleepycat.je.OperationStatus;
17 import com.sleepycat.util.RuntimeExceptionWrapper;
18
19 /**
20  * The Iterator returned by all stored collections.
21  *
22  * <p>While in general this class conforms to the {@link Iterator} interface,
23  * it is important to note that all iterators for stored collections must be
24  * explicitly closed with {@link #close()}. The static method {@link
25  * #close(java.util.Iterator)} allows calling close for all iterators without
26  * harm to iterators that are not from stored collections, and also avoids
27  * casting. If a stored iterator is not closed, unpredictable behavior
28  * including process death may result.</p>
29  *
30  * <p>This class implements the {@link Iterator} interface for all stored
31  * iterators. It also implements {@link ListIterator} because some list
32  * iterator methods apply to all stored iterators, for example, {@link
33  * #previous} and {@link #hasPrevious}. Other list iterator methods are always
34  * supported for lists, but for other types of collections are only supported
35  * under certain conditions. See {@link #nextIndex}, {@link #previousIndex},
36  * {@link #add} and {@link #set} for details.</p>
37  *
38  * <p>In addition, this class provides the following methods for stored
39  * collection iterators only. Note that the use of these methods is not
40  * compatible with the standard Java collections interface.</p>
41  * <ul>
42  * <li>{@link #close()}</li>
43  * <li>{@link #close(Iterator)}</li>
44  * <li>{@link #count()}</li>
45  * <li>{@link #getCollection}</li>
46  * <li>{@link #setReadModifyWrite}</li>
47  * <li>{@link #isReadModifyWrite}</li>
48  * </ul>
49  *
50  * @author Mark Hayes
51  */

52 public class StoredIterator implements BaseIterator, Cloneable JavaDoc {
53
54     /**
55      * Closes the given iterator using {@link #close()} if it is a {@link
56      * StoredIterator}. If the given iterator is not a {@link StoredIterator},
57      * this method does nothing.
58      *
59      * @param i is the iterator to close.
60      *
61      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
62      */

63     public static void close(Iterator JavaDoc i) {
64
65         if (i instanceof StoredIterator) {
66             ((StoredIterator) i).close();
67         }
68     }
69
70     private static final int MOVE_NEXT = 1;
71     private static final int MOVE_PREV = 2;
72     private static final int MOVE_FIRST = 3;
73
74     private boolean lockForWrite;
75     private StoredCollection coll;
76     private DataCursor cursor;
77     private int toNext;
78     private int toPrevious;
79     private int toCurrent;
80     private boolean writeAllowed;
81     private boolean setAndRemoveAllowed;
82     private Object JavaDoc currentData;
83
84     StoredIterator(StoredCollection coll, boolean writeAllowed,
85                    DataCursor joinCursor) {
86         try {
87             this.coll = coll;
88             this.writeAllowed = writeAllowed;
89             if (joinCursor == null)
90                 this.cursor = new DataCursor(coll.view, writeAllowed);
91             else
92                 this.cursor = joinCursor;
93             reset();
94         } catch (Exception JavaDoc e) {
95             try {
96                 /* Ensure that the cursor is closed. [#10516] */
97                 close();
98             } catch (Exception JavaDoc ignored) {
99         /* Klockwork - ok */
100         }
101             throw StoredContainer.convertException(e);
102         }
103     }
104
105     /**
106      * Returns whether write-locks will be obtained when reading with this
107      * cursor.
108      * Obtaining write-locks can prevent deadlocks when reading and then
109      * modifying data.
110      *
111      * @return the write-lock setting.
112      */

113     public final boolean isReadModifyWrite() {
114
115         return lockForWrite;
116     }
117
118     /**
119      * Changes whether write-locks will be obtained when reading with this
120      * cursor.
121      * Obtaining write-locks can prevent deadlocks when reading and then
122      * modifying data.
123      *
124      * @param lockForWrite the write-lock setting.
125      */

126     public void setReadModifyWrite(boolean lockForWrite) {
127
128         this.lockForWrite = lockForWrite;
129     }
130
131     // --- begin Iterator/ListIterator methods ---
132

133     /**
134      * Returns true if this iterator has more elements when traversing in the
135      * forward direction. False is returned if the iterator has been closed.
136      * This method conforms to the {@link Iterator#hasNext} interface.
137      *
138      * @return whether {@link #next()} will succeed.
139      *
140      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
141      */

142     public boolean hasNext() {
143
144         if (cursor == null) {
145             return false;
146         }
147         try {
148             if (toNext != 0) {
149                 OperationStatus status = move(toNext);
150                 if (status == OperationStatus.SUCCESS) {
151                     toNext = 0;
152                     toPrevious = MOVE_PREV;
153                     toCurrent = MOVE_PREV;
154                 }
155             }
156             return (toNext == 0);
157         } catch (Exception JavaDoc e) {
158             throw StoredContainer.convertException(e);
159         }
160     }
161
162     /**
163      * Returns true if this iterator has more elements when traversing in the
164      * reverse direction. It returns false if the iterator has been closed.
165      * This method conforms to the {@link ListIterator#hasPrevious} interface.
166      *
167      * @return whether {@link #previous()} will succeed.
168      *
169      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
170      */

171     public boolean hasPrevious() {
172
173         if (cursor == null) {
174             return false;
175         }
176         try {
177             if (toPrevious != 0) {
178                 OperationStatus status = move(toPrevious);
179                 if (status == OperationStatus.SUCCESS) {
180                     toPrevious = 0;
181                     toNext = MOVE_NEXT;
182                     toCurrent = MOVE_NEXT;
183                 }
184             }
185             return (toPrevious == 0);
186         } catch (Exception JavaDoc e) {
187             throw StoredContainer.convertException(e);
188         }
189     }
190
191     /**
192      * Returns the next element in the iteration.
193      * This method conforms to the {@link Iterator#next} interface.
194      *
195      * @return the next element.
196      *
197      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
198      * thrown.
199      */

200     public Object JavaDoc next() {
201
202         try {
203             if (toNext != 0) {
204                 OperationStatus status = move(toNext);
205                 if (status == OperationStatus.SUCCESS) {
206                     toNext = 0;
207                 }
208             }
209             if (toNext == 0) {
210                 currentData = coll.makeIteratorData(this, cursor);
211                 toNext = MOVE_NEXT;
212                 toPrevious = 0;
213                 toCurrent = 0;
214                 setAndRemoveAllowed = true;
215                 return currentData;
216             }
217             // else throw NoSuchElementException below
218
} catch (Exception JavaDoc e) {
219             throw StoredContainer.convertException(e);
220         }
221         throw new NoSuchElementException JavaDoc();
222     }
223
224     /**
225      * Returns the next element in the iteration.
226      * This method conforms to the {@link ListIterator#previous} interface.
227      *
228      * @return the previous element.
229      *
230      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
231      * thrown.
232      */

233     public Object JavaDoc previous() {
234
235         try {
236             if (toPrevious != 0) {
237                 OperationStatus status = move(toPrevious);
238                 if (status == OperationStatus.SUCCESS) {
239                     toPrevious = 0;
240                 }
241             }
242             if (toPrevious == 0) {
243                 currentData = coll.makeIteratorData(this, cursor);
244                 toPrevious = MOVE_PREV;
245                 toNext = 0;
246                 toCurrent = 0;
247                 setAndRemoveAllowed = true;
248                 return currentData;
249             }
250             // else throw NoSuchElementException below
251
} catch (Exception JavaDoc e) {
252             throw StoredContainer.convertException(e);
253         }
254         throw new NoSuchElementException JavaDoc();
255     }
256
257     /**
258      * Returns the index of the element that would be returned by a subsequent
259      * call to next.
260      * This method conforms to the {@link ListIterator#nextIndex} interface
261      * except that it returns Integer.MAX_VALUE for stored lists when
262      * positioned at the end of the list, rather than returning the list size
263      * as specified by the ListIterator interface. This is because the database
264      * size is not available.
265      *
266      * @return the next index.
267      *
268      * @throws UnsupportedOperationException if this iterator's collection does
269      * not use record number keys.
270      *
271      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
272      * thrown.
273      */

274     public int nextIndex() {
275
276         if (!coll.view.recNumAccess) {
277             throw new UnsupportedOperationException JavaDoc(
278                 "Record number access not supported");
279         }
280         try {
281             return hasNext() ? (cursor.getCurrentRecordNumber() -
282                                 coll.getIndexOffset())
283                              : Integer.MAX_VALUE;
284         } catch (Exception JavaDoc e) {
285             throw StoredContainer.convertException(e);
286         }
287     }
288
289     /**
290      * Returns the index of the element that would be returned by a subsequent
291      * call to previous.
292      * This method conforms to the {@link ListIterator#previousIndex}
293      * interface.
294      *
295      * @return the previous index.
296      *
297      * @throws UnsupportedOperationException if this iterator's collection does
298      * not use record number keys.
299      *
300      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
301      * thrown.
302      */

303     public int previousIndex() {
304
305         if (!coll.view.recNumAccess) {
306             throw new UnsupportedOperationException JavaDoc(
307                 "Record number access not supported");
308         }
309         try {
310             return hasPrevious() ? (cursor.getCurrentRecordNumber() -
311                                     coll.getIndexOffset())
312                                  : (-1);
313         } catch (Exception JavaDoc e) {
314             throw StoredContainer.convertException(e);
315         }
316     }
317
318     /**
319      * Replaces the last element returned by next or previous with the
320      * specified element (optional operation).
321      * This method conforms to the {@link ListIterator#set} interface.
322      *
323      * @param value the new value.
324      *
325      * @throws UnsupportedOperationException if the collection is a {@link
326      * StoredKeySet} (the set returned by {@link java.util.Map#keySet}), or if
327      * duplicates are sorted since this would change the iterator position, or
328      * if the collection is indexed, or if the collection is read-only.
329      *
330      * @throws IllegalArgumentException if an entity value binding is used and
331      * the primary key of the value given is different than the existing stored
332      * primary key.
333      *
334      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
335      * thrown.
336      */

337     public void set(Object JavaDoc value) {
338
339         if (!coll.hasValues()) throw new UnsupportedOperationException JavaDoc();
340         if (!setAndRemoveAllowed) throw new IllegalStateException JavaDoc();
341         try {
342             moveToCurrent();
343             cursor.putCurrent(value);
344         } catch (Exception JavaDoc e) {
345             throw StoredContainer.convertException(e);
346         }
347     }
348
349     /**
350      * Removes the last element that was returned by next or previous (optional
351      * operation).
352      * This method conforms to the {@link ListIterator#remove} interface except
353      * that when the collection is a list and the RECNO-RENUMBER access method
354      * is not used, list indices will not be renumbered.
355      *
356      * <p>Note that for the JE product, RECNO-RENUMBER databases are not
357      * supported, and therefore list indices are never renumbered by this
358      * method.</p>
359      *
360      * @throws UnsupportedOperationException if the collection is a sublist, or
361      * if the collection is read-only.
362      *
363      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
364      * thrown.
365      */

366     public void remove() {
367
368         if (!setAndRemoveAllowed) throw new IllegalStateException JavaDoc();
369         try {
370             moveToCurrent();
371             cursor.delete();
372             setAndRemoveAllowed = false;
373         } catch (Exception JavaDoc e) {
374             throw StoredContainer.convertException(e);
375         }
376     }
377
378     /**
379      * Inserts the specified element into the list or inserts a duplicate into
380      * other types of collections (optional operation).
381      * This method conforms to the {@link ListIterator#add} interface when
382      * the collection is a list and the RECNO-RENUMBER access method is used.
383      * Otherwise, this method may only be called when duplicates are allowed.
384      * If duplicates are unsorted, the new value will be inserted in the same
385      * manner as list elements.
386      * If duplicates are sorted, the new value will be inserted in sort order.
387      *
388      * <p>Note that for the JE product, RECNO-RENUMBER databases are not
389      * supported, and therefore this method may only be used to add
390      * duplicates.</p>
391      *
392      * @param value the new value.
393      *
394      * @throws UnsupportedOperationException if the collection is a sublist, or
395      * if the collection is indexed, or if the collection is read-only, or if
396      * the collection is a list and the RECNO-RENUMBER access method was not
397      * used, or if the collection is not a list and duplicates are not allowed.
398      *
399      * @throws IllegalStateException if the collection is empty and is not a
400      * list with RECNO-RENUMBER access.
401      *
402      * @throws IllegalArgumentException if a duplicate value is being added
403      * that already exists and duplicates are sorted.
404      *
405      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
406      * thrown.
407      */

408     public void add(Object JavaDoc value) {
409
410         coll.checkIterAddAllowed();
411         try {
412             OperationStatus status = OperationStatus.SUCCESS;
413             if (toNext != 0 && toPrevious != 0) { // database is empty
414
if (coll.view.keysRenumbered) { // recno-renumber database
415
/*
416                      * Close cursor during append and then reopen to support
417                      * CDB restriction that append may not be called with a
418                      * cursor open; note the append will still fail if the
419                      * application has another cursor open.
420                      */

421                     close();
422                     status = coll.view.append(value, null, null);
423                     cursor = new DataCursor(coll.view, writeAllowed);
424                     reset();
425                     next(); // move past new record
426
} else { // hash/btree with duplicates
427
throw new IllegalStateException JavaDoc(
428                         "Collection is empty, cannot add() duplicate");
429                 }
430             } else { // database is not empty
431
boolean putBefore = false;
432                 if (coll.view.keysRenumbered) { // recno-renumber database
433
moveToCurrent();
434                     if (hasNext()) {
435                         status = cursor.putBefore(value);
436                         putBefore = true;
437                     } else {
438                         status = cursor.putAfter(value);
439                     }
440                 } else { // hash/btree with duplicates
441
if (coll.areDuplicatesOrdered()) {
442                         status = cursor.putNoDupData(null, value, null, true);
443                     } else if (toNext == 0) {
444                         status = cursor.putBefore(value);
445                         putBefore = true;
446                     } else {
447                         status = cursor.putAfter(value);
448                     }
449                 }
450                 if (putBefore) {
451                     toPrevious = 0;
452                     toNext = MOVE_NEXT;
453                 }
454             }
455             if (status == OperationStatus.KEYEXIST) {
456                 throw new IllegalArgumentException JavaDoc("Duplicate value");
457             } else if (status != OperationStatus.SUCCESS) {
458                 throw new IllegalArgumentException JavaDoc("Could not insert: " +
459                                                     status);
460             }
461             setAndRemoveAllowed = false;
462         } catch (Exception JavaDoc e) {
463             throw StoredContainer.convertException(e);
464         }
465     }
466
467     // --- end Iterator/ListIterator methods ---
468

469     /**
470      * Resets cursor to an uninitialized state.
471      */

472     private void reset() {
473
474         toNext = MOVE_FIRST;
475         toPrevious = MOVE_PREV;
476         toCurrent = 0;
477         currentData = null;
478         /*
479      * Initialize cursor at beginning to avoid "initial previous == last"
480      * behavior when cursor is uninitialized.
481      *
482      * FindBugs whines about us ignoring the return value from hasNext().
483      */

484         hasNext();
485     }
486
487     /**
488      * Returns the number of elements having the same key value as the key
489      * value of the element last returned by next() or previous(). If no
490      * duplicates are allowed, 1 is always returned.
491      * This method does not exist in the standard {@link Iterator} or {@link
492      * ListIterator} interfaces.
493      *
494      * @return the number of duplicates.
495      *
496      * @throws IllegalStateException if next() or previous() has not been
497      * called for this iterator, or if remove() or add() were called after
498      * the last call to next() or previous().
499      */

500     public int count() {
501
502         if (!setAndRemoveAllowed) throw new IllegalStateException JavaDoc();
503         try {
504             moveToCurrent();
505             return cursor.count();
506         } catch (Exception JavaDoc e) {
507             throw StoredContainer.convertException(e);
508         }
509     }
510
511     /**
512      * Closes this iterator.
513      * This method does not exist in the standard {@link Iterator} or {@link
514      * ListIterator} interfaces.
515      *
516      * <p>After being closed, only the {@link #hasNext} and {@link
517      * #hasPrevious} methods may be called and these will return false. {@link
518      * #close()} may also be called again and will do nothing. If other
519      * methods are called a <code>NullPointerException</code> will generally be
520      * thrown.</p>
521      *
522      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
523      * thrown.
524      */

525     public void close() {
526
527         if (cursor != null) {
528             coll.closeCursor(cursor);
529             cursor = null;
530         }
531     }
532
533     /**
534      * Returns the collection associated with this iterator.
535      * This method does not exist in the standard {@link Iterator} or {@link
536      * ListIterator} interfaces.
537      *
538      * @return the collection associated with this iterator.
539      */

540     public final StoredCollection getCollection() {
541
542         return coll;
543     }
544
545     // --- begin BaseIterator methods ---
546

547     public final ListIterator JavaDoc dup() {
548
549         try {
550             StoredIterator o = (StoredIterator) super.clone();
551             o.cursor = cursor.cloneCursor();
552             return o;
553         } catch (Exception JavaDoc e) {
554             throw StoredContainer.convertException(e);
555         }
556     }
557
558     public final boolean isCurrentData(Object JavaDoc currentData) {
559
560         return (this.currentData == currentData);
561     }
562
563     public final boolean moveToIndex(int index) {
564
565         try {
566             OperationStatus status =
567                 cursor.getSearchKey(new Integer JavaDoc(index), null, lockForWrite);
568             setAndRemoveAllowed = (status == OperationStatus.SUCCESS);
569             return setAndRemoveAllowed;
570         } catch (Exception JavaDoc e) {
571             throw StoredContainer.convertException(e);
572         }
573     }
574
575     // --- end BaseIterator methods ---
576

577     private void moveToCurrent()
578         throws DatabaseException {
579
580         if (toCurrent != 0) {
581             move(toCurrent);
582             toCurrent = 0;
583         }
584     }
585
586     private OperationStatus move(int direction)
587         throws DatabaseException {
588
589         switch (direction) {
590             case MOVE_NEXT:
591                 if (coll.iterateDuplicates()) {
592                     return cursor.getNext(lockForWrite);
593                 } else {
594                     return cursor.getNextNoDup(lockForWrite);
595                 }
596             case MOVE_PREV:
597                 if (coll.iterateDuplicates()) {
598                     return cursor.getPrev(lockForWrite);
599                 } else {
600                     return cursor.getPrevNoDup(lockForWrite);
601                 }
602             case MOVE_FIRST:
603                 return cursor.getFirst(lockForWrite);
604             default:
605                 throw new IllegalArgumentException JavaDoc(String.valueOf(direction));
606         }
607     }
608 }
609
Popular Tags