1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: BasicIndex.java,v 1.2 2008/02/08 20:12:37 mark Exp $
7 */
8
9package com.sleepycat.persist;
10
11import com.sleepycat.bind.EntryBinding;
12import com.sleepycat.compat.DbCompat;
13import com.sleepycat.db.Cursor;
14import com.sleepycat.db.CursorConfig;
15import com.sleepycat.db.Database;
16import com.sleepycat.db.DatabaseConfig;
17import com.sleepycat.db.DatabaseEntry;
18import com.sleepycat.db.DatabaseException;
19import com.sleepycat.db.LockMode;
20import com.sleepycat.db.OperationStatus;
21import com.sleepycat.db.Transaction;
22import com.sleepycat.util.keyrange.KeyRange;
23import com.sleepycat.util.keyrange.RangeCursor;
24
25/**
26 * Implements EntityIndex using a ValueAdapter.  This class is abstract and
27 * does not implement get()/map()/sortedMap() because it doesn't have access
28 * to the entity binding.
29 *
30 * @author Mark Hayes
31 */
32abstract class BasicIndex<K,E> implements EntityIndex<K,E> {
33
34    static final DatabaseEntry NO_RETURN_ENTRY;
35    static {
36        NO_RETURN_ENTRY = new DatabaseEntry();
37        NO_RETURN_ENTRY.setPartial(0, 0, true);
38    }
39
40    Database db;
41    boolean transactional;
42    boolean sortedDups;
43    boolean locking;
44    Class<K> keyClass;
45    EntryBinding keyBinding;
46    KeyRange emptyRange;
47    ValueAdapter<K> keyAdapter;
48    ValueAdapter<E> entityAdapter;
49
50    BasicIndex(Database db,
51               Class<K> keyClass,
52               EntryBinding keyBinding,
53               ValueAdapter<E> entityAdapter)
54        throws DatabaseException {
55
56        this.db = db;
57        DatabaseConfig config = db.getConfig();
58        transactional = config.getTransactional();
59        sortedDups = config.getSortedDuplicates();
60        locking =
61            DbCompat.getInitializeLocking(db.getEnvironment().getConfig());
62
63        this.keyClass = keyClass;
64        this.keyBinding = keyBinding;
65        this.entityAdapter = entityAdapter;
66
67        emptyRange = new KeyRange(config.getBtreeComparator());
68        keyAdapter = new KeyValueAdapter(keyClass, keyBinding);
69    }
70
71    /*
72     * Of the EntityIndex methods only get()/map()/sortedMap() are not
73     * implemented here and therefore must be implemented by subclasses.
74     */
75
76    public boolean contains(K key)
77        throws DatabaseException {
78
79        return contains(null, key, null);
80    }
81
82    public boolean contains(Transaction txn, K key, LockMode lockMode)
83        throws DatabaseException {
84
85        DatabaseEntry keyEntry = new DatabaseEntry();
86        DatabaseEntry dataEntry = NO_RETURN_ENTRY;
87        keyBinding.objectToEntry(key, keyEntry);
88
89        OperationStatus status = db.get(txn, keyEntry, dataEntry, lockMode);
90        return (status == OperationStatus.SUCCESS);
91    }
92
93    public long count()
94        throws DatabaseException {
95
96        if (DbCompat.DATABASE_COUNT) {
97            return DbCompat.getDatabaseCount(db);
98        } else {
99            long count = 0;
100            DatabaseEntry key = NO_RETURN_ENTRY;
101            DatabaseEntry data = NO_RETURN_ENTRY;
102            CursorConfig cursorConfig = locking ?
103                CursorConfig.READ_UNCOMMITTED : null;
104            Cursor cursor = db.openCursor(null, cursorConfig);
105            try {
106                OperationStatus status = cursor.getFirst(key, data, null);
107                while (status == OperationStatus.SUCCESS) {
108                    if (sortedDups) {
109                        count += cursor.count();
110                    } else {
111                        count += 1;
112                    }
113                    status = cursor.getNextNoDup(key, data, null);
114                }
115            } finally {
116                cursor.close();
117            }
118            return count;
119        }
120    }
121
122    public boolean delete(K key)
123        throws DatabaseException {
124
125        return delete(null, key);
126    }
127
128    public boolean delete(Transaction txn, K key)
129        throws DatabaseException {
130
131        DatabaseEntry keyEntry = new DatabaseEntry();
132        keyBinding.objectToEntry(key, keyEntry);
133
134        OperationStatus status = db.delete(txn, keyEntry);
135        return (status == OperationStatus.SUCCESS);
136    }
137
138    public EntityCursor<K> keys()
139        throws DatabaseException {
140
141        return keys(null, null);
142    }
143
144    public EntityCursor<K> keys(Transaction txn, CursorConfig config)
145        throws DatabaseException {
146
147        return cursor(txn, emptyRange, keyAdapter, config);
148    }
149
150    public EntityCursor<E> entities()
151        throws DatabaseException {
152
153        return cursor(null, emptyRange, entityAdapter, null);
154    }
155
156    public EntityCursor<E> entities(Transaction txn,
157                                    CursorConfig config)
158        throws DatabaseException {
159
160        return cursor(txn, emptyRange, entityAdapter, config);
161    }
162
163    public EntityCursor<K> keys(K fromKey, boolean fromInclusive,
164                                K toKey, boolean toInclusive)
165        throws DatabaseException {
166
167        return cursor(null, fromKey, fromInclusive, toKey, toInclusive,
168                      keyAdapter, null);
169    }
170
171    public EntityCursor<K> keys(Transaction txn,
172                                K fromKey,
173				boolean fromInclusive,
174                                K toKey,
175				boolean toInclusive,
176                                CursorConfig config)
177        throws DatabaseException {
178
179        return cursor(txn, fromKey, fromInclusive, toKey, toInclusive,
180                      keyAdapter, config);
181    }
182
183    public EntityCursor<E> entities(K fromKey, boolean fromInclusive,
184                                    K toKey, boolean toInclusive)
185        throws DatabaseException {
186
187        return cursor(null, fromKey, fromInclusive, toKey, toInclusive,
188                      entityAdapter, null);
189    }
190
191    public EntityCursor<E> entities(Transaction txn,
192                                    K fromKey,
193				    boolean fromInclusive,
194                                    K toKey,
195				    boolean toInclusive,
196                                    CursorConfig config)
197        throws DatabaseException {
198
199        return cursor(txn, fromKey, fromInclusive, toKey, toInclusive,
200                      entityAdapter, config);
201    }
202
203    /*
204    public ForwardCursor<K> unsortedKeys(KeySelector<K> selector)
205        throws DatabaseException {
206
207        return unsortedKeys(null, selector, null);
208    }
209
210    public ForwardCursor<K> unsortedKeys(Transaction txn,
211                                         KeySelector<K> selector,
212                                         CursorConfig config)
213        throws DatabaseException {
214
215        throw new UnsupportedOperationException();
216    }
217
218    public ForwardCursor<E> unsortedEntities(KeySelector<K> selector)
219        throws DatabaseException {
220
221        return unsortedEntities(null, selector, null);
222    }
223
224    public ForwardCursor<E> unsortedEntities(Transaction txn,
225                                             KeySelector<K> selector,
226                                             CursorConfig config)
227        throws DatabaseException {
228
229        throw new UnsupportedOperationException();
230    }
231    */
232
233    private <V> EntityCursor<V> cursor(Transaction txn,
234                                       K fromKey,
235				       boolean fromInclusive,
236                                       K toKey,
237				       boolean toInclusive,
238                                       ValueAdapter<V> adapter,
239                                       CursorConfig config)
240        throws DatabaseException {
241
242        DatabaseEntry fromEntry = null;
243        if (fromKey != null) {
244            fromEntry = new DatabaseEntry();
245            keyBinding.objectToEntry(fromKey, fromEntry);
246        }
247        DatabaseEntry toEntry = null;
248        if (toKey != null) {
249            toEntry = new DatabaseEntry();
250            keyBinding.objectToEntry(toKey, toEntry);
251        }
252        KeyRange range = emptyRange.subRange
253            (fromEntry, fromInclusive, toEntry, toInclusive);
254        return cursor(txn, range, adapter, config);
255    }
256
257    private <V> EntityCursor<V> cursor(Transaction txn,
258                                       KeyRange range,
259                                       ValueAdapter<V> adapter,
260                                       CursorConfig config)
261        throws DatabaseException {
262
263        Cursor cursor = db.openCursor(txn, config);
264        RangeCursor rangeCursor =
265            new RangeCursor(range, null/*pkRange*/, sortedDups, cursor);
266        return new BasicCursor<V>(rangeCursor, adapter, isUpdateAllowed());
267    }
268
269    abstract boolean isUpdateAllowed();
270}
271