KickJava   Java API By Example, From Geeks To Geeks.

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


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

8
9 package com.sleepycat.collections;
10
11 import com.sleepycat.bind.EntityBinding;
12 import com.sleepycat.bind.EntryBinding;
13 import com.sleepycat.compat.DbCompat;
14 import com.sleepycat.je.CursorConfig;
15 import com.sleepycat.je.Database;
16 import com.sleepycat.je.DatabaseConfig;
17 import com.sleepycat.je.DatabaseEntry;
18 import com.sleepycat.je.DatabaseException;
19 import com.sleepycat.je.Environment;
20 import com.sleepycat.je.JoinConfig;
21 import com.sleepycat.je.OperationStatus;
22 import com.sleepycat.je.SecondaryConfig;
23 import com.sleepycat.je.SecondaryDatabase;
24 import com.sleepycat.je.SecondaryKeyCreator;
25 import com.sleepycat.je.Transaction;
26 import com.sleepycat.util.RuntimeExceptionWrapper;
27 import com.sleepycat.util.keyrange.KeyRange;
28 import com.sleepycat.util.keyrange.KeyRangeException;
29
30 /**
31  * Represents a Berkeley DB database and adds support for indices, bindings and
32  * key ranges.
33  *
34  * <p>This class defines a view and takes care of reading and updating indices,
35  * calling bindings, constraining access to a key range, etc.</p>
36  *
37  * @author Mark Hayes
38  */

39 final class DataView implements Cloneable JavaDoc {
40
41     Database db;
42     SecondaryDatabase secDb;
43     CurrentTransaction currentTxn;
44     KeyRange range;
45     EntryBinding keyBinding;
46     EntryBinding valueBinding;
47     EntityBinding entityBinding;
48     PrimaryKeyAssigner keyAssigner;
49     SecondaryKeyCreator secKeyCreator;
50     CursorConfig cursorConfig; // Used for all operations via this view
51
boolean writeAllowed; // Read-write view
52
boolean ordered; // Not a HASH Db
53
boolean recNumAllowed; // QUEUE, RECNO, or BTREE-RECNUM Db
54
boolean recNumAccess; // recNumAllowed && using a rec num binding
55
boolean btreeRecNumDb; // BTREE-RECNUM Db
56
boolean btreeRecNumAccess; // recNumAccess && BTREE-RECNUM Db
57
boolean recNumRenumber; // RECNO-RENUM Db
58
boolean keysRenumbered; // recNumRenumber || btreeRecNumAccess
59
boolean dupsAllowed; // Dups configured
60
boolean dupsOrdered; // Sorted dups configured
61
boolean transactional; // Db is transactional
62
boolean readUncommittedAllowed; // Read-uncommited is optional in DB-CORE
63

64     /*
65      * If duplicatesView is called, dupsView will be true and dupsKey will be
66      * the secondary key used as the "single key" range. dupRange will be set
67      * as the range of the primary key values if subRange is subsequently
68      * called, to further narrow the view.
69      */

70     DatabaseEntry dupsKey;
71     boolean dupsView;
72     KeyRange dupsRange;
73
74     /**
75      * Creates a view for a given database and bindings. The initial key range
76      * of the view will be open.
77      */

78     DataView(Database database, EntryBinding keyBinding,
79              EntryBinding valueBinding, EntityBinding entityBinding,
80              boolean writeAllowed, PrimaryKeyAssigner keyAssigner)
81         throws IllegalArgumentException JavaDoc {
82
83         if (database == null) {
84             throw new IllegalArgumentException JavaDoc("database is null");
85         }
86         db = database;
87         try {
88             currentTxn =
89                 CurrentTransaction.getInstanceInternal(db.getEnvironment());
90             DatabaseConfig dbConfig;
91             if (db instanceof SecondaryDatabase) {
92                 secDb = (SecondaryDatabase) database;
93                 SecondaryConfig secConfig = secDb.getSecondaryConfig();
94                 secKeyCreator = secConfig.getKeyCreator();
95                 dbConfig = secConfig;
96             } else {
97                 dbConfig = db.getConfig();
98             }
99             ordered = !DbCompat.isTypeHash(dbConfig);
100             recNumAllowed = DbCompat.isTypeQueue(dbConfig) ||
101                             DbCompat.isTypeRecno(dbConfig) ||
102                             DbCompat.getBtreeRecordNumbers(dbConfig);
103             recNumRenumber = DbCompat.getRenumbering(dbConfig);
104             dupsAllowed = DbCompat.getSortedDuplicates(dbConfig) ||
105                           DbCompat.getUnsortedDuplicates(dbConfig);
106             dupsOrdered = DbCompat.getSortedDuplicates(dbConfig);
107             transactional = currentTxn.isTxnMode() &&
108                             dbConfig.getTransactional();
109             readUncommittedAllowed = DbCompat.getReadUncommitted(dbConfig);
110             btreeRecNumDb = recNumAllowed && DbCompat.isTypeBtree(dbConfig);
111             range = new KeyRange(dbConfig.getBtreeComparator());
112         } catch (DatabaseException e) {
113             throw new RuntimeExceptionWrapper(e);
114         }
115         this.writeAllowed = writeAllowed;
116         this.keyBinding = keyBinding;
117         this.valueBinding = valueBinding;
118         this.entityBinding = entityBinding;
119         this.keyAssigner = keyAssigner;
120         cursorConfig = CursorConfig.DEFAULT;
121
122         if (valueBinding != null && entityBinding != null)
123             throw new IllegalArgumentException JavaDoc(
124                 "both valueBinding and entityBinding are non-null");
125
126         if (keyBinding instanceof com.sleepycat.bind.RecordNumberBinding) {
127             if (!recNumAllowed) {
128                 throw new IllegalArgumentException JavaDoc(
129                     "RecordNumberBinding requires DB_BTREE/DB_RECNUM, " +
130                     "DB_RECNO, or DB_QUEUE");
131             }
132             recNumAccess = true;
133             if (btreeRecNumDb) {
134                 btreeRecNumAccess = true;
135             }
136         }
137         keysRenumbered = recNumRenumber || btreeRecNumAccess;
138     }
139
140     /**
141      * Clones the view.
142      */

143     private DataView cloneView() {
144
145         try {
146             return (DataView) super.clone();
147         } catch (CloneNotSupportedException JavaDoc willNeverOccur) {
148             throw new IllegalStateException JavaDoc();
149         }
150     }
151
152     /**
153      * Return a new key-set view derived from this view by setting the
154      * entity and value binding to null.
155      *
156      * @return the derived view.
157      */

158     DataView keySetView() {
159
160         if (keyBinding == null) {
161             throw new UnsupportedOperationException JavaDoc("must have keyBinding");
162         }
163         DataView view = cloneView();
164         view.valueBinding = null;
165         view.entityBinding = null;
166         return view;
167     }
168
169     /**
170      * Return a new value-set view derived from this view by setting the
171      * key binding to null.
172      *
173      * @return the derived view.
174      */

175     DataView valueSetView() {
176
177         if (valueBinding == null && entityBinding == null) {
178             throw new UnsupportedOperationException JavaDoc(
179                 "must have valueBinding or entityBinding");
180         }
181         DataView view = cloneView();
182         view.keyBinding = null;
183         return view;
184     }
185
186     /**
187      * Return a new value-set view for single key range.
188      *
189      * @param singleKey the single key value.
190      *
191      * @return the derived view.
192      *
193      * @throws DatabaseException if a database problem occurs.
194      *
195      * @throws KeyRangeException if the specified range is not within the
196      * current range.
197      */

198     DataView valueSetView(Object JavaDoc singleKey)
199         throws DatabaseException, KeyRangeException {
200
201         /*
202          * Must do subRange before valueSetView since the latter clears the
203          * key binding needed for the former.
204          */

205         KeyRange singleKeyRange = subRange(range, singleKey);
206         DataView view = valueSetView();
207         view.range = singleKeyRange;
208         return view;
209     }
210
211     /**
212      * Return a new value-set view for key range, optionally changing
213      * the key binding.
214      */

215     DataView subView(Object JavaDoc beginKey, boolean beginInclusive,
216                      Object JavaDoc endKey, boolean endInclusive,
217                      EntryBinding keyBinding)
218         throws DatabaseException, KeyRangeException {
219
220         DataView view = cloneView();
221         view.setRange(beginKey, beginInclusive, endKey, endInclusive);
222         if (keyBinding != null) view.keyBinding = keyBinding;
223         return view;
224     }
225
226     /**
227      * Return a new duplicates view for a given secondary key.
228      */

229     DataView duplicatesView(Object JavaDoc secondaryKey,
230                             EntryBinding primaryKeyBinding)
231         throws DatabaseException, KeyRangeException {
232
233         if (!isSecondary()) {
234             throw new UnsupportedOperationException JavaDoc
235                 ("Only allowed for maps on secondary databases");
236         }
237         if (dupsView) {
238             throw new IllegalStateException JavaDoc();
239         }
240         DataView view = cloneView();
241         view.range = subRange(view.range, secondaryKey);
242         view.dupsKey = view.range.getSingleKey();
243         view.dupsView = true;
244         view.keyBinding = primaryKeyBinding;
245         return view;
246     }
247
248     /**
249      * Returns a new view with a specified cursor configuration.
250      */

251     DataView configuredView(CursorConfig config) {
252
253         DataView view = cloneView();
254         view.cursorConfig = (config != null) ?
255             DbCompat.cloneCursorConfig(config) : CursorConfig.DEFAULT;
256         return view;
257     }
258
259     /**
260      * Returns the current transaction for the view or null if the environment
261      * is non-transactional.
262      */

263     CurrentTransaction getCurrentTxn() {
264
265         return transactional ? currentTxn : null;
266     }
267
268     /**
269      * Sets this view's range to a subrange with the given parameters.
270      */

271     private void setRange(Object JavaDoc beginKey, boolean beginInclusive,
272                           Object JavaDoc endKey, boolean endInclusive)
273         throws DatabaseException, KeyRangeException {
274
275         KeyRange useRange = useSubRange();
276         useRange = subRange
277             (useRange, beginKey, beginInclusive, endKey, endInclusive);
278         if (dupsView) {
279             dupsRange = useRange;
280         } else {
281             range = useRange;
282         }
283     }
284
285     /**
286      * Returns the key thang for a single key range, or null if a single key
287      * range is not used.
288      */

289     DatabaseEntry getSingleKeyThang() {
290
291         return range.getSingleKey();
292     }
293
294     /**
295      * Returns the environment for the database.
296      */

297     final Environment getEnv() {
298
299         return currentTxn.getEnvironment();
300     }
301
302     /**
303      * Returns whether this is a view on a secondary database rather
304      * than directly on a primary database.
305      */

306     final boolean isSecondary() {
307
308         return (secDb != null);
309     }
310
311     /**
312      * Returns whether no records are present in the view.
313      */

314     boolean isEmpty()
315         throws DatabaseException {
316
317         DataCursor cursor = new DataCursor(this, false);
318         try {
319             return cursor.getFirst(false) != OperationStatus.SUCCESS;
320         } finally {
321             cursor.close();
322         }
323     }
324
325     /**
326      * Appends a value and returns the new key. If a key assigner is used
327      * it assigns the key, otherwise a QUEUE or RECNO database is required.
328      */

329     OperationStatus append(Object JavaDoc value, Object JavaDoc[] retPrimaryKey,
330                            Object JavaDoc[] retValue)
331         throws DatabaseException {
332
333         /*
334          * Flags will be NOOVERWRITE if used with assigner, or APPEND
335          * otherwise.
336          * Requires: if value param, value or entity binding
337          * Requires: if retPrimaryKey, primary key binding (no index).
338          * Requires: if retValue, value or entity binding
339          */

340         DatabaseEntry keyThang = new DatabaseEntry();
341         DatabaseEntry valueThang = new DatabaseEntry();
342         useValue(value, valueThang, null);
343         OperationStatus status;
344         if (keyAssigner != null) {
345             keyAssigner.assignKey(keyThang);
346             if (!range.check(keyThang)) {
347                 throw new IllegalArgumentException JavaDoc(
348                     "assigned key out of range");
349             }
350             DataCursor cursor = new DataCursor(this, true);
351             try {
352                 status = cursor.getCursor().putNoOverwrite(keyThang,
353                                                            valueThang);
354             } finally {
355                 cursor.close();
356             }
357         } else {
358             /* Assume QUEUE/RECNO access method. */
359             if (currentTxn.isCDBCursorOpen(db)) {
360                 throw new IllegalStateException JavaDoc(
361                   "cannot open CDB write cursor when read cursor is open");
362             }
363             status = DbCompat.append(db, useTransaction(),
364                                      keyThang, valueThang);
365             if (status == OperationStatus.SUCCESS && !range.check(keyThang)) {
366                 db.delete(useTransaction(), keyThang);
367                 throw new IllegalArgumentException JavaDoc(
368                     "appended record number out of range");
369             }
370         }
371         if (status == OperationStatus.SUCCESS) {
372             returnPrimaryKeyAndValue(keyThang, valueThang,
373                                      retPrimaryKey, retValue);
374         }
375         return status;
376     }
377
378     /**
379      * Returns the current transaction if the database is transaction, or null
380      * if the database is not transactional or there is no current transaction.
381      */

382     Transaction useTransaction() {
383         return transactional ? currentTxn.getTransaction() : null;
384     }
385
386     /**
387      * Deletes all records in the current range.
388      */

389     void clear()
390         throws DatabaseException {
391
392         DataCursor cursor = new DataCursor(this, true);
393         try {
394             OperationStatus status = OperationStatus.SUCCESS;
395             while (status == OperationStatus.SUCCESS) {
396                 if (keysRenumbered) {
397                     status = cursor.getFirst(true);
398                 } else {
399                     status = cursor.getNext(true);
400                 }
401                 if (status == OperationStatus.SUCCESS) {
402                     cursor.delete();
403                 }
404             }
405         } finally {
406             cursor.close();
407         }
408     }
409
410     /**
411      * Returns a cursor for this view that reads only records having the
412      * specified index key values.
413      */

414     DataCursor join(DataView[] indexViews, Object JavaDoc[] indexKeys,
415                     JoinConfig joinConfig)
416         throws DatabaseException {
417
418         DataCursor joinCursor = null;
419         DataCursor[] indexCursors = new DataCursor[indexViews.length];
420         try {
421             for (int i = 0; i < indexViews.length; i += 1) {
422                 indexCursors[i] = new DataCursor(indexViews[i], false);
423                 indexCursors[i].getSearchKey(indexKeys[i], null, false);
424             }
425             joinCursor = new DataCursor(this, indexCursors, joinConfig, true);
426             return joinCursor;
427         } finally {
428             if (joinCursor == null) {
429                 // An exception is being thrown, so close cursors we opened.
430
for (int i = 0; i < indexCursors.length; i += 1) {
431                     if (indexCursors[i] != null) {
432                         try { indexCursors[i].close(); }
433                         catch (Exception JavaDoc e) {
434                 /* FindBugs, this is ok. */
435             }
436                     }
437                 }
438             }
439         }
440     }
441
442     /**
443      * Returns a cursor for this view that reads only records having the
444      * index key values at the specified cursors.
445      */

446     DataCursor join(DataCursor[] indexCursors, JoinConfig joinConfig)
447         throws DatabaseException {
448
449         return new DataCursor(this, indexCursors, joinConfig, false);
450     }
451
452     /**
453      * Returns primary key and value if return parameters are non-null.
454      */

455     private void returnPrimaryKeyAndValue(DatabaseEntry keyThang,
456                                           DatabaseEntry valueThang,
457                                           Object JavaDoc[] retPrimaryKey,
458                                           Object JavaDoc[] retValue)
459         throws DatabaseException {
460
461         // Requires: if retPrimaryKey, primary key binding (no index).
462
// Requires: if retValue, value or entity binding
463

464         if (retPrimaryKey != null) {
465             if (keyBinding == null) {
466                 throw new IllegalArgumentException JavaDoc(
467                     "returning key requires primary key binding");
468             } else if (isSecondary()) {
469                 throw new IllegalArgumentException JavaDoc(
470                     "returning key requires unindexed view");
471             } else {
472                 retPrimaryKey[0] = keyBinding.entryToObject(keyThang);
473             }
474         }
475         if (retValue != null) {
476             retValue[0] = makeValue(keyThang, valueThang);
477         }
478     }
479
480     /**
481      * Populates the key entry and returns whether the key is within range.
482      */

483     boolean useKey(Object JavaDoc key, Object JavaDoc value, DatabaseEntry keyThang,
484                    KeyRange checkRange)
485         throws DatabaseException {
486
487         if (key != null) {
488             if (keyBinding == null) {
489                 throw new IllegalArgumentException JavaDoc(
490                     "non-null key with null key binding");
491             }
492             keyBinding.objectToEntry(key, keyThang);
493         } else {
494             if (value == null) {
495                 throw new IllegalArgumentException JavaDoc(
496                     "null key and null value");
497             }
498             if (entityBinding == null) {
499                 throw new IllegalStateException JavaDoc(
500                     "EntityBinding required to derive key from value");
501             }
502             if (!dupsView && isSecondary()) {
503                 DatabaseEntry primaryKeyThang = new DatabaseEntry();
504                 entityBinding.objectToKey(value, primaryKeyThang);
505                 DatabaseEntry valueThang = new DatabaseEntry();
506                 entityBinding.objectToData(value, valueThang);
507                 secKeyCreator.createSecondaryKey(secDb, primaryKeyThang,
508                                                  valueThang, keyThang);
509             } else {
510                 entityBinding.objectToKey(value, keyThang);
511             }
512         }
513         if (recNumAccess && DbCompat.getRecordNumber(keyThang) <= 0) {
514             return false;
515         }
516         if (checkRange != null && !checkRange.check(keyThang)) {
517             return false;
518         }
519         return true;
520     }
521
522     /**
523      * Returns whether data keys can be derived from the value/entity binding
524      * of this view, which determines whether a value/entity object alone is
525      * sufficient for operations that require keys.
526      */

527     final boolean canDeriveKeyFromValue() {
528
529         return (entityBinding != null);
530     }
531
532     /**
533      * Populates the value entry and throws an exception if the primary key
534      * would be changed via an entity binding.
535      */

536     void useValue(Object JavaDoc value, DatabaseEntry valueThang,
537                   DatabaseEntry checkKeyThang)
538         throws DatabaseException {
539
540         if (value != null) {
541             if (valueBinding != null) {
542                 valueBinding.objectToEntry(value, valueThang);
543             } else if (entityBinding != null) {
544                 entityBinding.objectToData(value, valueThang);
545                 if (checkKeyThang != null) {
546                     DatabaseEntry thang = new DatabaseEntry();
547                     entityBinding.objectToKey(value, thang);
548                     if (!KeyRange.equalBytes(thang, checkKeyThang)) {
549                         throw new IllegalArgumentException JavaDoc(
550                             "cannot change primary key");
551                     }
552                 }
553             } else {
554                 throw new IllegalArgumentException JavaDoc(
555                     "non-null value with null value/entity binding");
556             }
557         } else {
558             valueThang.setData(KeyRange.ZERO_LENGTH_BYTE_ARRAY);
559             valueThang.setOffset(0);
560             valueThang.setSize(0);
561         }
562     }
563
564     /**
565      * Converts a key entry to a key object.
566      */

567     Object JavaDoc makeKey(DatabaseEntry keyThang, DatabaseEntry priKeyThang) {
568
569         if (keyBinding == null) {
570             throw new UnsupportedOperationException JavaDoc();
571         } else {
572             DatabaseEntry thang = dupsView ? priKeyThang : keyThang;
573             if (thang.getSize() == 0) {
574                 return null;
575             } else {
576                 return keyBinding.entryToObject(thang);
577             }
578         }
579     }
580
581     /**
582      * Converts a key-value entry pair to a value object.
583      */

584     Object JavaDoc makeValue(DatabaseEntry primaryKeyThang, DatabaseEntry valueThang) {
585
586         Object JavaDoc value;
587         if (valueBinding != null) {
588             value = valueBinding.entryToObject(valueThang);
589         } else if (entityBinding != null) {
590             value = entityBinding.entryToObject(primaryKeyThang,
591                                                     valueThang);
592         } else {
593             throw new UnsupportedOperationException JavaDoc(
594                 "requires valueBinding or entityBinding");
595         }
596         return value;
597     }
598
599     /**
600      * Intersects the given key and the current range.
601      */

602     KeyRange subRange(KeyRange useRange, Object JavaDoc singleKey)
603         throws DatabaseException, KeyRangeException {
604
605         return useRange.subRange(makeRangeKey(singleKey));
606     }
607
608     /**
609      * Intersects the given range and the current range.
610      */

611     KeyRange subRange(KeyRange useRange,
612                       Object JavaDoc beginKey, boolean beginInclusive,
613                       Object JavaDoc endKey, boolean endInclusive)
614         throws DatabaseException, KeyRangeException {
615
616         if (beginKey == endKey && beginInclusive && endInclusive) {
617             return subRange(useRange, beginKey);
618         }
619         if (!ordered) {
620             throw new UnsupportedOperationException JavaDoc(
621                     "Cannot use key ranges on an unsorted database");
622         }
623         DatabaseEntry beginThang =
624             (beginKey != null) ? makeRangeKey(beginKey) : null;
625         DatabaseEntry endThang =
626             (endKey != null) ? makeRangeKey(endKey) : null;
627
628         return useRange.subRange(beginThang, beginInclusive,
629                                  endThang, endInclusive);
630     }
631
632     /**
633      * Returns the range to use for sub-ranges. Returns range if this is not a
634      * dupsView, or the dupsRange if this is a dupsView, creating dupsRange if
635      * necessary.
636      */

637     KeyRange useSubRange()
638         throws DatabaseException {
639
640         if (dupsView) {
641             synchronized (this) {
642                 if (dupsRange == null) {
643                     DatabaseConfig config =
644                         secDb.getPrimaryDatabase().getConfig();
645                     dupsRange = new KeyRange(config.getBtreeComparator());
646                 }
647             }
648             return dupsRange;
649         } else {
650             return range;
651         }
652     }
653
654     /**
655      * Given a key object, make a key entry that can be used in a range.
656      */

657     private DatabaseEntry makeRangeKey(Object JavaDoc key)
658         throws DatabaseException {
659
660         DatabaseEntry thang = new DatabaseEntry();
661         if (keyBinding != null) {
662             useKey(key, null, thang, null);
663         } else {
664             useKey(null, key, thang, null);
665         }
666         return thang;
667     }
668 }
669
Popular Tags