KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2000,2006 Oracle. All rights reserved.
5  *
6  * $Id: StoredList.java,v 1.47 2006/12/04 15:00:47 cwl Exp $
7  */

8
9 package com.sleepycat.collections;
10
11 import java.util.Collection JavaDoc;
12 import java.util.Iterator JavaDoc;
13 import java.util.List JavaDoc;
14 import java.util.ListIterator JavaDoc;
15
16 import com.sleepycat.bind.EntityBinding;
17 import com.sleepycat.bind.EntryBinding;
18 import com.sleepycat.bind.RecordNumberBinding;
19 import com.sleepycat.je.Database;
20 import com.sleepycat.je.DatabaseEntry;
21 import com.sleepycat.je.DatabaseException;
22 import com.sleepycat.je.OperationStatus;
23 import com.sleepycat.util.keyrange.KeyRangeException;
24
25 /**
26  * A List view of a {@link Database}.
27  *
28  * <p>For all stored lists the keys of the underlying Database
29  * must have record number format, and therefore the store or index must be a
30  * RECNO, RECNO-RENUMBER, QUEUE, or BTREE-RECNUM database. Only RECNO-RENUMBER
31  * allows true list behavior where record numbers are renumbered following the
32  * position of an element that is added or removed. For the other access
33  * methods (RECNO, QUEUE, and BTREE-RECNUM), stored Lists are most useful as
34  * read-only collections where record numbers are not required to be
35  * sequential.</p>
36  *
37  * <p>In addition to the standard List methods, this class provides the
38  * following methods for stored lists only. Note that the use of these methods
39  * is not compatible with the standard Java collections interface.</p>
40  * <ul>
41  * <li>{@link #append(Object)}</li>
42  * </ul>
43  *
44  * @author Mark Hayes
45  */

46 public class StoredList extends StoredCollection implements List JavaDoc {
47
48     private static final EntryBinding DEFAULT_KEY_BINDING =
49         new IndexKeyBinding(1);
50
51     private int baseIndex = 1;
52     private boolean isSubList;
53
54     /**
55      * Creates a list view of a {@link Database}.
56      *
57      * @param database is the Database underlying the new collection.
58      *
59      * @param valueBinding is the binding used to translate between value
60      * buffers and value objects.
61      *
62      * @param writeAllowed is true to create a read-write collection or false
63      * to create a read-only collection.
64      *
65      * @throws IllegalArgumentException if formats are not consistently
66      * defined or a parameter is invalid.
67      *
68      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
69      * thrown.
70      */

71     public StoredList(Database database, EntryBinding valueBinding,
72                       boolean writeAllowed) {
73
74         super(new DataView(database, DEFAULT_KEY_BINDING, valueBinding, null,
75                            writeAllowed, null));
76     }
77
78     /**
79      * Creates a list entity view of a {@link Database}.
80      *
81      * @param database is the Database underlying the new collection.
82      *
83      * @param valueEntityBinding is the binding used to translate between
84      * key/value buffers and entity value objects.
85      *
86      * @param writeAllowed is true to create a read-write collection or false
87      * to create a read-only collection.
88      *
89      * @throws IllegalArgumentException if formats are not consistently
90      * defined or a parameter is invalid.
91      *
92      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
93      * thrown.
94      */

95     public StoredList(Database database, EntityBinding valueEntityBinding,
96                       boolean writeAllowed) {
97
98         super(new DataView(database, DEFAULT_KEY_BINDING, null,
99                            valueEntityBinding, writeAllowed, null));
100     }
101
102     /**
103      * Creates a list view of a {@link Database} with a {@link
104      * PrimaryKeyAssigner}. Writing is allowed for the created list.
105      *
106      * @param database is the Database underlying the new collection.
107      *
108      * @param valueBinding is the binding used to translate between value
109      * buffers and value objects.
110      *
111      * @param keyAssigner is used by the {@link #add} and {@link #append}
112      * methods to assign primary keys.
113      *
114      * @throws IllegalArgumentException if formats are not consistently
115      * defined or a parameter is invalid.
116      *
117      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
118      * thrown.
119      */

120     public StoredList(Database database, EntryBinding valueBinding,
121                       PrimaryKeyAssigner keyAssigner) {
122
123         super(new DataView(database, DEFAULT_KEY_BINDING, valueBinding,
124                            null, true, keyAssigner));
125     }
126
127     /**
128      * Creates a list entity view of a {@link Database} with a {@link
129      * PrimaryKeyAssigner}. Writing is allowed for the created list.
130      *
131      * @param database is the Database underlying the new collection.
132      *
133      * @param valueEntityBinding is the binding used to translate between
134      * key/value buffers and entity value objects.
135      *
136      * @param keyAssigner is used by the {@link #add} and {@link #append}
137      * methods to assign primary keys.
138      *
139      * @throws IllegalArgumentException if formats are not consistently
140      * defined or a parameter is invalid.
141      *
142      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
143      * thrown.
144      */

145     public StoredList(Database database, EntityBinding valueEntityBinding,
146                       PrimaryKeyAssigner keyAssigner) {
147
148         super(new DataView(database, DEFAULT_KEY_BINDING, null,
149                            valueEntityBinding, true, keyAssigner));
150     }
151
152     private StoredList(DataView view, int baseIndex) {
153
154         super(view);
155         this.baseIndex = baseIndex;
156         this.isSubList = true;
157     }
158
159     /**
160      * Inserts the specified element at the specified position in this list
161      * (optional operation).
162      * This method conforms to the {@link List#add(int, Object)} interface.
163      *
164      * @throws UnsupportedOperationException if the collection is a sublist, or
165      * if the collection is indexed, or if the collection is read-only, or if
166      * the RECNO-RENUMBER access method was not used.
167      *
168      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
169      * thrown.
170      */

171     public void add(int index, Object JavaDoc value) {
172
173         checkIterAddAllowed();
174         DataCursor cursor = null;
175         boolean doAutoCommit = beginAutoCommit();
176         try {
177             cursor = new DataCursor(view, true);
178             OperationStatus status =
179                 cursor.getSearchKey(new Long JavaDoc(index), null, false);
180             if (status == OperationStatus.SUCCESS) {
181                 cursor.putBefore(value);
182                 closeCursor(cursor);
183             } else {
184                 closeCursor(cursor);
185                 cursor = null;
186                 view.append(value, null, null);
187             }
188             commitAutoCommit(doAutoCommit);
189         } catch (Exception JavaDoc e) {
190             closeCursor(cursor);
191             throw handleException(e, doAutoCommit);
192         }
193     }
194
195     /**
196      * Appends the specified element to the end of this list (optional
197      * operation).
198      * This method conforms to the {@link List#add(Object)} interface.
199      *
200      * @throws UnsupportedOperationException if the collection is a sublist, or
201      * if the collection is indexed, or if the collection is read-only, or if
202      * the RECNO-RENUMBER access method was not used.
203      *
204      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
205      * thrown.
206      */

207     public boolean add(Object JavaDoc value) {
208
209         checkIterAddAllowed();
210         boolean doAutoCommit = beginAutoCommit();
211         try {
212             view.append(value, null, null);
213             commitAutoCommit(doAutoCommit);
214             return true;
215         } catch (Exception JavaDoc e) {
216             throw handleException(e, doAutoCommit);
217         }
218     }
219
220     /**
221      * Appends a given value returning the newly assigned index.
222      * If a {@link com.sleepycat.collections.PrimaryKeyAssigner} is associated
223      * with Store for this list, it will be used to assigned the returned
224      * index. Otherwise the Store must be a QUEUE or RECNO database and the
225      * next available record number is assigned as the index. This method does
226      * not exist in the standard {@link List} interface.
227      *
228      * @param value the value to be appended.
229      *
230      * @return the assigned index.
231      *
232      * @throws UnsupportedOperationException if the collection is indexed, or
233      * if the collection is read-only, or if the Store has no {@link
234      * com.sleepycat.collections.PrimaryKeyAssigner} and is not a QUEUE or
235      * RECNO database.
236      *
237      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
238      * thrown.
239      */

240     public int append(Object JavaDoc value) {
241
242         boolean doAutoCommit = beginAutoCommit();
243         try {
244             Object JavaDoc[] key = new Object JavaDoc[1];
245             view.append(value, key, null);
246             commitAutoCommit(doAutoCommit);
247             return ((Number JavaDoc) key[0]).intValue();
248         } catch (Exception JavaDoc e) {
249             throw handleException(e, doAutoCommit);
250         }
251     }
252
253     void checkIterAddAllowed()
254         throws UnsupportedOperationException JavaDoc {
255
256         if (isSubList) {
257             throw new UnsupportedOperationException JavaDoc("cannot add to subList");
258         }
259         if (!view.keysRenumbered) { // RECNO-RENUM
260
throw new UnsupportedOperationException JavaDoc(
261                 "requires renumbered keys");
262         }
263     }
264
265     /**
266      * Inserts all of the elements in the specified collection into this list
267      * at the specified position (optional operation).
268      * This method conforms to the {@link List#addAll(int, Collection)}
269      * interface.
270      *
271      * @throws UnsupportedOperationException if the collection is a sublist, or
272      * if the collection is indexed, or if the collection is read-only, or if
273      * the RECNO-RENUMBER access method was not used.
274      *
275      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
276      * thrown.
277      */

278     public boolean addAll(int index, Collection JavaDoc coll) {
279
280         checkIterAddAllowed();
281         DataCursor cursor = null;
282     Iterator JavaDoc i = null;
283         boolean doAutoCommit = beginAutoCommit();
284         try {
285             i = storedOrExternalIterator(coll);
286             if (!i.hasNext()) {
287                 return false;
288             }
289             cursor = new DataCursor(view, true);
290             OperationStatus status =
291                 cursor.getSearchKey(new Long JavaDoc(index), null, false);
292             if (status == OperationStatus.SUCCESS) {
293                 while (i.hasNext()) {
294                     cursor.putBefore(i.next());
295                 }
296                 closeCursor(cursor);
297             } else {
298                 closeCursor(cursor);
299                 cursor = null;
300                 while (i.hasNext()) {
301                     view.append(i.next(), null, null);
302                 }
303             }
304             StoredIterator.close(i);
305             commitAutoCommit(doAutoCommit);
306             return true;
307         } catch (Exception JavaDoc e) {
308             closeCursor(cursor);
309             StoredIterator.close(i);
310             throw handleException(e, doAutoCommit);
311         }
312     }
313
314     /**
315      * Returns true if this list contains the specified element.
316      * This method conforms to the {@link List#contains} interface.
317      *
318      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
319      * thrown.
320      */

321     public boolean contains(Object JavaDoc value) {
322
323         return containsValue(value);
324     }
325
326     /**
327      * Returns the element at the specified position in this list.
328      * This method conforms to the {@link List#get} interface.
329      *
330      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
331      * thrown.
332      */

333     public Object JavaDoc get(int index) {
334
335         return super.get(new Long JavaDoc(index));
336     }
337
338     /**
339      * Returns the index in this list of the first occurrence of the specified
340      * element, or -1 if this list does not contain this element.
341      * This method conforms to the {@link List#indexOf} interface.
342      *
343      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
344      * thrown.
345      */

346     public int indexOf(Object JavaDoc value) {
347
348         return indexOf(value, true);
349     }
350
351     /**
352      * Returns the index in this list of the last occurrence of the specified
353      * element, or -1 if this list does not contain this element.
354      * This method conforms to the {@link List#lastIndexOf} interface.
355      *
356      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
357      * thrown.
358      */

359     public int lastIndexOf(Object JavaDoc value) {
360
361         return indexOf(value, false);
362     }
363
364     private int indexOf(Object JavaDoc value, boolean findFirst) {
365
366         DataCursor cursor = null;
367         try {
368             cursor = new DataCursor(view, false);
369             OperationStatus status = cursor.findValue(value, findFirst);
370             return (status == OperationStatus.SUCCESS)
371                     ? (cursor.getCurrentRecordNumber() - baseIndex)
372                     : (-1);
373         } catch (Exception JavaDoc e) {
374             throw StoredContainer.convertException(e);
375         } finally {
376             closeCursor(cursor);
377         }
378     }
379
380     int getIndexOffset() {
381
382         return baseIndex;
383     }
384
385     /**
386      * Returns a list iterator of the elements in this list (in proper
387      * sequence).
388      * The iterator will be read-only if the collection is read-only.
389      * This method conforms to the {@link List#listIterator()} interface.
390      *
391      * <p>For information on cursor stability and iterator block size, see
392      * {@link #iterator()}.</p>
393      *
394      * @return a {@link ListIterator} for this collection.
395      *
396      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
397      * thrown.
398      *
399      * @see #isWriteAllowed
400      */

401     public ListIterator JavaDoc listIterator() {
402
403         return blockIterator();
404     }
405
406     /**
407      * Returns a list iterator of the elements in this list (in proper
408      * sequence), starting at the specified position in this list.
409      * The iterator will be read-only if the collection is read-only.
410      * This method conforms to the {@link List#listIterator(int)} interface.
411      *
412      * <p>For information on cursor stability and iterator block size, see
413      * {@link #iterator()}.</p>
414      *
415      * @return a {@link ListIterator} for this collection.
416      *
417      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
418      * thrown.
419      *
420      * @see #isWriteAllowed
421      */

422     public ListIterator JavaDoc listIterator(int index) {
423
424         BlockIterator i = blockIterator();
425         if (i.moveToIndex(index)) {
426             return i;
427         } else {
428             throw new IndexOutOfBoundsException JavaDoc(String.valueOf(index));
429         }
430     }
431
432     /**
433      * Removes the element at the specified position in this list (optional
434      * operation).
435      * This method conforms to the {@link List#remove(int)} interface.
436      *
437      * @throws UnsupportedOperationException if the collection is a sublist, or
438      * if the collection is read-only.
439      *
440      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
441      * thrown.
442      */

443     public Object JavaDoc remove(int index) {
444
445         try {
446             Object JavaDoc[] oldVal = new Object JavaDoc[1];
447             removeKey(new Long JavaDoc(index), oldVal);
448             return oldVal[0];
449         } catch (IllegalArgumentException JavaDoc e) {
450             throw new IndexOutOfBoundsException JavaDoc(e.getMessage());
451         }
452     }
453
454     /**
455      * Removes the first occurrence in this list of the specified element
456      * (optional operation).
457      * This method conforms to the {@link List#remove(Object)} interface.
458      *
459      * @throws UnsupportedOperationException if the collection is a sublist, or
460      * if the collection is read-only.
461      *
462      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
463      * thrown.
464      */

465     public boolean remove(Object JavaDoc value) {
466
467         return removeValue(value);
468     }
469
470     /**
471      * Replaces the element at the specified position in this list with the
472      * specified element (optional operation).
473      * This method conforms to the {@link List#set} interface.
474      *
475      * @throws UnsupportedOperationException if the collection is indexed, or
476      * if the collection is read-only.
477      *
478      * @throws IllegalArgumentException if an entity value binding is used and
479      * the primary key of the value given is different than the existing stored
480      * primary key.
481      *
482      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
483      * thrown.
484      */

485     public Object JavaDoc set(int index, Object JavaDoc value) {
486
487         try {
488             return put(new Long JavaDoc(index), value);
489         } catch (IllegalArgumentException JavaDoc e) {
490             throw new IndexOutOfBoundsException JavaDoc(e.getMessage());
491         }
492     }
493
494     /**
495      * Returns a view of the portion of this list between the specified
496      * fromIndex, inclusive, and toIndex, exclusive.
497      * Note that add() and remove() may not be called for the returned sublist.
498      * This method conforms to the {@link List#subList} interface.
499      *
500      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
501      * thrown.
502      */

503     public List JavaDoc subList(int fromIndex, int toIndex) {
504
505         if (fromIndex < 0 || fromIndex > toIndex) {
506             throw new IndexOutOfBoundsException JavaDoc(String.valueOf(fromIndex));
507         }
508         try {
509             int newBaseIndex = baseIndex + fromIndex;
510             return new StoredList(
511                 view.subView(new Long JavaDoc(fromIndex), true,
512                              new Long JavaDoc(toIndex), false,
513                              new IndexKeyBinding(newBaseIndex)),
514                 newBaseIndex);
515         } catch (KeyRangeException e) {
516             throw new IndexOutOfBoundsException JavaDoc(e.getMessage());
517         } catch (Exception JavaDoc e) {
518             throw StoredContainer.convertException(e);
519         }
520     }
521
522     /**
523      * Compares the specified object with this list for equality.
524      * A value comparison is performed by this method and the stored values
525      * are compared rather than calling the equals() method of each element.
526      * This method conforms to the {@link List#equals} interface.
527      *
528      * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
529      * thrown.
530      */

531     public boolean equals(Object JavaDoc other) {
532
533         if (!(other instanceof List JavaDoc)) return false;
534         List JavaDoc otherList = (List JavaDoc) other;
535         StoredIterator i1 = null;
536         ListIterator JavaDoc i2 = null;
537         try {
538             i1 = storedIterator();
539             i2 = storedOrExternalListIterator(otherList);
540             while (i1.hasNext()) {
541                 if (!i2.hasNext()) return false;
542                 if (i1.nextIndex() != i2.nextIndex()) return false;
543                 Object JavaDoc o1 = i1.next();
544                 Object JavaDoc o2 = i2.next();
545                 if (o1 == null) {
546                     if (o2 != null) return false;
547                 } else {
548                     if (!o1.equals(o2)) return false;
549                 }
550             }
551             if (i2.hasNext()) return false;
552             return true;
553         } finally {
554         if (i1 != null) {
555         i1.close();
556         }
557             StoredIterator.close(i2);
558         }
559     }
560
561     /**
562      * Returns a StoredIterator if the given collection is a StoredCollection,
563      * else returns a regular/external ListIterator. The iterator returned
564      * should be closed with the static method StoredIterator.close(Iterator).
565      */

566     final ListIterator JavaDoc storedOrExternalListIterator(List JavaDoc list) {
567
568         if (list instanceof StoredCollection) {
569             return ((StoredCollection) list).storedIterator();
570         } else {
571             return list.listIterator();
572         }
573     }
574
575     /*
576      * Add this in to keep FindBugs from whining at us about implementing
577      * equals(), but not hashCode().
578      */

579     public int hashCode() {
580     return super.hashCode();
581     }
582
583     Object JavaDoc makeIteratorData(BaseIterator iterator,
584                             DatabaseEntry keyEntry,
585                             DatabaseEntry priKeyEntry,
586                             DatabaseEntry valueEntry) {
587
588         return view.makeValue(priKeyEntry, valueEntry);
589     }
590
591     boolean hasValues() {
592
593         return true;
594     }
595
596     private static class IndexKeyBinding extends RecordNumberBinding {
597
598         private int baseIndex;
599
600         private IndexKeyBinding(int baseIndex) {
601
602             this.baseIndex = baseIndex;
603         }
604
605         public Object JavaDoc entryToObject(DatabaseEntry data) {
606
607             return new Long JavaDoc(entryToRecordNumber(data) - baseIndex);
608         }
609
610         public void objectToEntry(Object JavaDoc object, DatabaseEntry data) {
611
612             recordNumberToEntry(((Number JavaDoc) object).intValue() + baseIndex,
613                                 data);
614         }
615     }
616 }
617
Popular Tags