KickJava   Java API By Example, From Geeks To Geeks.

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


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

8
9 package com.sleepycat.collections;
10
11 import java.util.ArrayList JavaDoc;
12 import java.util.Arrays JavaDoc;
13 import java.util.Collection JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16
17 import com.sleepycat.compat.DbCompat;
18 import com.sleepycat.je.CursorConfig;
19 import com.sleepycat.je.DatabaseEntry;
20 import com.sleepycat.je.DatabaseException;
21 import com.sleepycat.je.JoinConfig;
22 import com.sleepycat.je.OperationStatus;
23
24 /**
25  * A abstract base class for all stored collections. This class, and its
26  * base class {@link StoredContainer}, provide implementations of most methods
27  * in the {@link Collection} interface. Other methods, such as {@link #add}
28  * and {@link #remove}, are provided by concrete classes that extend this
29  * class.
30  *
31  * <p>In addition, this class provides the following methods for stored
32  * collections only. Note that the use of these methods is not compatible with
33  * the standard Java collections interface.</p>
34  * <ul>
35  * <li>{@link #getIteratorBlockSize}</li>
36  * <li>{@link #setIteratorBlockSize}</li>
37  * <li>{@link #storedIterator()}</li>
38  * <li>{@link #storedIterator(boolean)}</li>
39  * <li>{@link #join}</li>
40  * <li>{@link #toList()}</li>
41  * </ul>
42  *
43  * @author Mark Hayes
44  */

45 public abstract class StoredCollection extends StoredContainer
46     implements Collection JavaDoc {
47
48     /**
49      * The default number of records read at one time by iterators.
50      * @see #setIteratorBlockSize
51      */

52     public static final int DEFAULT_ITERATOR_BLOCK_SIZE = 10;
53
54     private int iteratorBlockSize = DEFAULT_ITERATOR_BLOCK_SIZE;
55
56     StoredCollection(DataView view) {
57
58         super(view);
59     }
60
61     /**
62      * Returns the number of records read at one time by iterators returned by
63      * the {@link #iterator} method. By default this value is {@link
64      * #DEFAULT_ITERATOR_BLOCK_SIZE}.
65      */

66     public int getIteratorBlockSize() {
67
68         return iteratorBlockSize;
69     }
70
71     /**
72      * Changes the number of records read at one time by iterators returned by
73      * the {@link #iterator} method. By default this value is {@link
74      * #DEFAULT_ITERATOR_BLOCK_SIZE}.
75      *
76      * @throws IllegalArgumentException if the blockSize is less than two.
77      */

78     public void setIteratorBlockSize(int blockSize) {
79
80         if (blockSize < 2) {
81             throw new IllegalArgumentException JavaDoc
82                 ("blockSize is less than two: " + blockSize);
83         }
84
85         iteratorBlockSize = blockSize;
86     }
87
88     final boolean add(Object JavaDoc key, Object JavaDoc value) {
89
90         DataCursor cursor = null;
91         boolean doAutoCommit = beginAutoCommit();
92         try {
93             cursor = new DataCursor(view, true);
94             OperationStatus status =
95                 cursor.putNoDupData(key, value, null, false);
96             closeCursor(cursor);
97             commitAutoCommit(doAutoCommit);
98             return (status == OperationStatus.SUCCESS);
99         } catch (Exception JavaDoc e) {
100             closeCursor(cursor);
101             throw handleException(e, doAutoCommit);
102         }
103     }
104
105     BlockIterator blockIterator() {
106         return new BlockIterator(this, isWriteAllowed(), iteratorBlockSize);
107     }
108
109     /**
110      * Returns an iterator over the elements in this collection.
111      * The iterator will be read-only if the collection is read-only.
112      * This method conforms to the {@link Collection#iterator} interface.
113      *
114      * <p>The iterator returned by this method does not keep a database cursor
115      * open and therefore it does not need to be closed. It reads blocks of
116      * records as needed, opening and closing a cursor to read each block of
117      * records. The number of records per block is 10 by default and can be
118      * changed with {@link #setIteratorBlockSize}.</p>
119      *
120      * <p>Because this iterator does not keep a cursor open, if it is used
121      * without transactions, the iterator does not have <em>cursor
122      * stability</em> characteristics. In other words, the record at the
123      * current iterator position can be changed or deleted by another thread.
124      * To prevent this from happening, call this method within a transaction or
125      * use the {@link #storedIterator()} method instead.</p>
126      *
127      * @return a standard {@link Iterator} for this collection.
128      *
129      * @see #isWriteAllowed
130      */

131     public Iterator JavaDoc iterator() {
132         return blockIterator();
133     }
134
135     /**
136      * Returns an iterator over the elements in this collection.
137      * The iterator will be read-only if the collection is read-only.
138      * This method does not exist in the standard {@link Collection} interface.
139      *
140      * <p><strong>Warning:</strong> The iterator returned must be explicitly
141      * closed using {@link StoredIterator#close()} or {@link
142      * StoredIterator#close(java.util.Iterator)} to release the underlying
143      * database cursor resources.</p>
144      *
145      * @return a {@link StoredIterator} for this collection.
146      *
147      * @see #isWriteAllowed
148      */

149     public StoredIterator storedIterator() {
150
151         return storedIterator(isWriteAllowed());
152     }
153
154     /**
155      * Returns a read or read-write iterator over the elements in this
156      * collection.
157      * This method does not exist in the standard {@link Collection} interface.
158      *
159      * <p><strong>Warning:</strong> The iterator returned must be explicitly
160      * closed using {@link StoredIterator#close()} or {@link
161      * StoredIterator#close(java.util.Iterator)} to release the underlying
162      * database cursor resources.</p>
163      *
164      * @param writeAllowed is true to open a read-write iterator or false to
165      * open a read-only iterator. If the collection is read-only the iterator
166      * will always be read-only.
167      *
168      * @return a {@link StoredIterator} for this collection.
169      *
170      * @throws IllegalStateException if writeAllowed is true but the collection
171      * is read-only.
172      *
173      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
174      * thrown.
175      *
176      * @see #isWriteAllowed
177      */

178     public StoredIterator storedIterator(boolean writeAllowed) {
179
180         try {
181             return new StoredIterator(this, writeAllowed && isWriteAllowed(),
182                                       null);
183         } catch (Exception JavaDoc e) {
184             throw StoredContainer.convertException(e);
185         }
186     }
187
188     /**
189      * @deprecated Please use {@link #storedIterator()} or {@link
190      * #storedIterator(boolean)} instead. Because the iterator returned must
191      * be closed, the method name {@code iterator} is confusing since standard
192      * Java iterators do not need to be closed.
193      */

194     public StoredIterator iterator(boolean writeAllowed) {
195         
196         return storedIterator(writeAllowed);
197     }
198
199     /**
200      * Returns an array of all the elements in this collection.
201      * This method conforms to the {@link Collection#toArray()} interface.
202      *
203      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
204      * thrown.
205      */

206     public Object JavaDoc[] toArray() {
207
208         ArrayList JavaDoc list = new ArrayList JavaDoc();
209         StoredIterator i = storedIterator();
210         try {
211             while (i.hasNext()) {
212                 list.add(i.next());
213             }
214         } finally {
215             i.close();
216         }
217         return list.toArray();
218     }
219
220     /**
221      * Returns an array of all the elements in this collection whose runtime
222      * type is that of the specified array.
223      * This method conforms to the {@link Collection#toArray(Object[])}
224      * interface.
225      *
226      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
227      * thrown.
228      */

229     public Object JavaDoc[] toArray(Object JavaDoc[] a) {
230
231         int j = 0;
232         StoredIterator i = storedIterator();
233         try {
234             while (j < a.length && i.hasNext()) {
235                 a[j++] = i.next();
236             }
237             if (j < a.length) {
238                 a[j] = null;
239             } else if (i.hasNext()) {
240                 ArrayList JavaDoc list = new ArrayList JavaDoc(Arrays.asList(a));
241                 while (i.hasNext()) {
242                     list.add(i.next());
243                 }
244                 a = list.toArray(a);
245             }
246         } finally {
247             i.close();
248         }
249         return a;
250     }
251
252     /**
253      * Returns true if this collection contains all of the elements in the
254      * specified collection.
255      * This method conforms to the {@link Collection#containsAll} interface.
256      *
257      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
258      * thrown.
259      */

260     public boolean containsAll(Collection JavaDoc coll) {
261     Iterator JavaDoc i = storedOrExternalIterator(coll);
262         try {
263             while (i.hasNext()) {
264                 if (!contains(i.next())) {
265                     return false;
266                 }
267             }
268         } finally {
269             StoredIterator.close(i);
270         }
271     return true;
272     }
273
274     /**
275      * Adds all of the elements in the specified collection to this collection
276      * (optional operation).
277      * This method calls the {@link #add(Object)} method of the concrete
278      * collection class, which may or may not be supported.
279      * This method conforms to the {@link Collection#addAll} interface.
280      *
281      * @throws UnsupportedOperationException if the collection is read-only, or
282      * if the collection is indexed, or if the add method is not supported by
283      * the concrete collection.
284      *
285      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
286      * thrown.
287      */

288     public boolean addAll(Collection JavaDoc coll) {
289     Iterator JavaDoc i = null;
290         boolean doAutoCommit = beginAutoCommit();
291         try {
292             i = storedOrExternalIterator(coll);
293             boolean changed = false;
294             while (i.hasNext()) {
295                 if (add(i.next())) {
296                     changed = true;
297                 }
298             }
299             StoredIterator.close(i);
300             commitAutoCommit(doAutoCommit);
301             return changed;
302         } catch (Exception JavaDoc e) {
303             StoredIterator.close(i);
304             throw handleException(e, doAutoCommit);
305         }
306     }
307
308     /**
309      * Removes all this collection's elements that are also contained in the
310      * specified collection (optional operation).
311      * This method conforms to the {@link Collection#removeAll} interface.
312      *
313      * @throws UnsupportedOperationException if the collection is read-only.
314      *
315      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
316      * thrown.
317      */

318     public boolean removeAll(Collection JavaDoc coll) {
319
320         return removeAll(coll, true);
321     }
322
323     /**
324      * Retains only the elements in this collection that are contained in the
325      * specified collection (optional operation).
326      * This method conforms to the {@link Collection#removeAll} interface.
327      *
328      * @throws UnsupportedOperationException if the collection is read-only.
329      *
330      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
331      * thrown.
332      */

333     public boolean retainAll(Collection JavaDoc coll) {
334
335         return removeAll(coll, false);
336     }
337
338     private boolean removeAll(Collection JavaDoc coll, boolean ifExistsInColl) {
339     StoredIterator i = null;
340         boolean doAutoCommit = beginAutoCommit();
341         try {
342             boolean changed = false;
343             i = storedIterator();
344             while (i.hasNext()) {
345                 if (ifExistsInColl == coll.contains(i.next())) {
346                     i.remove();
347                     changed = true;
348                 }
349             }
350             i.close();
351             commitAutoCommit(doAutoCommit);
352             return changed;
353         } catch (Exception JavaDoc e) {
354             if (i != null) {
355                 i.close();
356             }
357             throw handleException(e, doAutoCommit);
358         }
359     }
360
361     /**
362      * Compares the specified object with this collection for equality.
363      * A value comparison is performed by this method and the stored values
364      * are compared rather than calling the equals() method of each element.
365      * This method conforms to the {@link Collection#equals} interface.
366      *
367      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
368      * thrown.
369      */

370     public boolean equals(Object JavaDoc other) {
371
372         if (other instanceof Collection JavaDoc) {
373             Collection JavaDoc otherColl = StoredCollection.copyCollection(other);
374             StoredIterator i = storedIterator();
375             try {
376                 while (i.hasNext()) {
377                     if (!otherColl.remove(i.next())) {
378                         return false;
379                     }
380                 }
381                 return otherColl.isEmpty();
382             } finally {
383                 i.close();
384             }
385         } else {
386             return false;
387         }
388     }
389
390     /*
391      * Add this in to keep FindBugs from whining at us about implementing
392      * equals(), but not hashCode().
393      */

394     public int hashCode() {
395     return super.hashCode();
396     }
397
398     /**
399      * Returns a copy of this collection as an ArrayList. This is the same as
400      * {@link #toArray()} but returns a collection instead of an array.
401      *
402      * @return an {@link ArrayList} containing a copy of all elements in this
403      * collection.
404      *
405      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
406      * thrown.
407      */

408     public List JavaDoc toList() {
409
410         ArrayList JavaDoc list = new ArrayList JavaDoc();
411         StoredIterator i = storedIterator();
412         try {
413             while (i.hasNext()) list.add(i.next());
414             return list;
415         } finally {
416             i.close();
417         }
418     }
419
420     /**
421      * Converts the collection to a string representation for debugging.
422      * WARNING: The returned string may be very large.
423      *
424      * @return the string representation.
425      *
426      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
427      * thrown.
428      */

429     public String JavaDoc toString() {
430     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
431     buf.append("[");
432     StoredIterator i = storedIterator();
433         try {
434             while (i.hasNext()) {
435                 if (buf.length() > 1) buf.append(',');
436                 buf.append(i.next().toString());
437             }
438             buf.append(']');
439             return buf.toString();
440         } finally {
441             i.close();
442         }
443     }
444
445     // Inherit javadoc
446
public int size() {
447
448         boolean countDups = iterateDuplicates();
449         if (DbCompat.DATABASE_COUNT && countDups && !view.range.hasBound()) {
450             try {
451                 return (int) DbCompat.getDatabaseCount(view.db);
452             } catch (Exception JavaDoc e) {
453                 throw StoredContainer.convertException(e);
454             }
455         } else {
456             int count = 0;
457             CursorConfig cursorConfig = view.currentTxn.isLockingMode() ?
458                 CursorConfig.READ_UNCOMMITTED : null;
459             DataCursor cursor = null;
460             try {
461                 cursor = new DataCursor(view, false, cursorConfig);
462                 OperationStatus status = cursor.getFirst(false);
463                 while (status == OperationStatus.SUCCESS) {
464                     if (countDups) {
465                         count += cursor.count();
466                     } else {
467                         count += 1;
468                     }
469                     status = cursor.getNextNoDup(false);
470                 }
471             } catch (Exception JavaDoc e) {
472                 throw StoredContainer.convertException(e);
473             } finally {
474                 closeCursor(cursor);
475             }
476             return count;
477         }
478     }
479
480     /**
481      * Returns an iterator representing an equality join of the indices and
482      * index key values specified.
483      * This method does not exist in the standard {@link Collection} interface.
484      *
485      * <p><strong>Warning:</strong> The iterator returned must be explicitly
486      * closed using {@link StoredIterator#close()} or {@link
487      * StoredIterator#close(java.util.Iterator)} to release the underlying
488      * database cursor resources.</p>
489      *
490      * <p>The returned iterator supports only the two methods: hasNext() and
491      * next(). All other methods will throw UnsupportedOperationException.</p>
492      *
493      * @param indices is an array of indices with elements corresponding to
494      * those in the indexKeys array.
495      *
496      * @param indexKeys is an array of index key values identifying the
497      * elements to be selected.
498      *
499      * @param joinConfig is the join configuration, or null to use the
500      * default configuration.
501      *
502      * @return an iterator over the elements in this collection that match
503      * all specified index key values.
504      *
505      * @throws IllegalArgumentException if this collection is indexed or if a
506      * given index does not have the same store as this collection.
507      *
508      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
509      * thrown.
510      */

511     public StoredIterator join(StoredContainer[] indices, Object JavaDoc[] indexKeys,
512                                JoinConfig joinConfig) {
513
514         try {
515             DataView[] indexViews = new DataView[indices.length];
516             for (int i = 0; i < indices.length; i += 1) {
517                 indexViews[i] = indices[i].view;
518             }
519             DataCursor cursor = view.join(indexViews, indexKeys, joinConfig);
520             return new StoredIterator(this, false, cursor);
521         } catch (Exception JavaDoc e) {
522             throw StoredContainer.convertException(e);
523         }
524     }
525
526     final Object JavaDoc getFirstOrLast(boolean doGetFirst) {
527
528         DataCursor cursor = null;
529         try {
530             cursor = new DataCursor(view, false);
531             OperationStatus status;
532             if (doGetFirst) {
533                 status = cursor.getFirst(false);
534             } else {
535                 status = cursor.getLast(false);
536             }
537             return (status == OperationStatus.SUCCESS) ?
538                     makeIteratorData(null, cursor) : null;
539         } catch (Exception JavaDoc e) {
540             throw StoredContainer.convertException(e);
541         } finally {
542             closeCursor(cursor);
543         }
544     }
545
546     Object JavaDoc makeIteratorData(BaseIterator iterator, DataCursor cursor) {
547
548         return makeIteratorData(iterator,
549                                 cursor.getKeyThang(),
550                                 cursor.getPrimaryKeyThang(),
551                                 cursor.getValueThang());
552     }
553
554     abstract Object JavaDoc makeIteratorData(BaseIterator iterator,
555                                      DatabaseEntry keyEntry,
556                                      DatabaseEntry priKeyEntry,
557                                      DatabaseEntry valueEntry);
558
559     abstract boolean hasValues();
560
561     boolean iterateDuplicates() {
562
563         return true;
564     }
565
566     void checkIterAddAllowed()
567         throws UnsupportedOperationException JavaDoc {
568
569         if (!areDuplicatesAllowed()) {
570             throw new UnsupportedOperationException JavaDoc("duplicates required");
571         }
572     }
573
574     int getIndexOffset() {
575
576         return 0;
577     }
578
579     private static Collection JavaDoc copyCollection(Object JavaDoc other) {
580
581         if (other instanceof StoredCollection) {
582             return ((StoredCollection) other).toList();
583         } else {
584             return new ArrayList JavaDoc((Collection JavaDoc) other);
585         }
586     }
587 }
588
Popular Tags