summaryrefslogtreecommitdiff
path: root/db/java
diff options
context:
space:
mode:
authorjbj <devnull@localhost>2004-10-16 01:31:54 +0000
committerjbj <devnull@localhost>2004-10-16 01:31:54 +0000
commitd03f220fde879509cab2ac1c73b71b7efb52b737 (patch)
tree1e34bfadac0a6618d0e9a7933bad90063a785acf /db/java
parent2dc699bfe049b9319ea3719f604d25940ff52004 (diff)
downloadlibrpm-tizen-d03f220fde879509cab2ac1c73b71b7efb52b737.tar.gz
librpm-tizen-d03f220fde879509cab2ac1c73b71b7efb52b737.tar.bz2
librpm-tizen-d03f220fde879509cab2ac1c73b71b7efb52b737.zip
... and in with the New ...
CVS patchset: 7471 CVS date: 2004/10/16 01:31:54
Diffstat (limited to 'db/java')
-rw-r--r--db/java/src/com/sleepycat/bind/ByteArrayBinding.java43
-rw-r--r--db/java/src/com/sleepycat/bind/EntityBinding.java49
-rw-r--r--db/java/src/com/sleepycat/bind/EntryBinding.java38
-rw-r--r--db/java/src/com/sleepycat/bind/RecordNumberBinding.java70
-rw-r--r--db/java/src/com/sleepycat/bind/package.html7
-rw-r--r--db/java/src/com/sleepycat/bind/serial/ClassCatalog.java72
-rw-r--r--db/java/src/com/sleepycat/bind/serial/SerialBinding.java130
-rw-r--r--db/java/src/com/sleepycat/bind/serial/SerialInput.java75
-rw-r--r--db/java/src/com/sleepycat/bind/serial/SerialOutput.java114
-rw-r--r--db/java/src/com/sleepycat/bind/serial/SerialSerialBinding.java117
-rw-r--r--db/java/src/com/sleepycat/bind/serial/SerialSerialKeyCreator.java143
-rw-r--r--db/java/src/com/sleepycat/bind/serial/StoredClassCatalog.java446
-rw-r--r--db/java/src/com/sleepycat/bind/serial/TupleSerialBinding.java115
-rw-r--r--db/java/src/com/sleepycat/bind/serial/TupleSerialKeyCreator.java137
-rw-r--r--db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledBinding.java93
-rw-r--r--db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledKeyCreator.java75
-rw-r--r--db/java/src/com/sleepycat/bind/serial/package.html6
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/BooleanBinding.java75
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/ByteBinding.java74
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/CharacterBinding.java74
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/DoubleBinding.java75
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/FloatBinding.java74
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/IntegerBinding.java74
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/LongBinding.java74
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/MarshalledTupleEntry.java45
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/MarshalledTupleKeyEntity.java71
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/ShortBinding.java74
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/StringBinding.java76
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleBinding.java179
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleInput.java482
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleInputBinding.java46
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleMarshalledBinding.java70
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleOutput.java398
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleTupleBinding.java96
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleTupleKeyCreator.java105
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledBinding.java94
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledKeyCreator.java75
-rw-r--r--db/java/src/com/sleepycat/bind/tuple/package.html6
-rw-r--r--db/java/src/com/sleepycat/collections/CurrentTransaction.java433
-rw-r--r--db/java/src/com/sleepycat/collections/DataCursor.java690
-rw-r--r--db/java/src/com/sleepycat/collections/DataView.java598
-rw-r--r--db/java/src/com/sleepycat/collections/KeyRange.java299
-rw-r--r--db/java/src/com/sleepycat/collections/KeyRangeException.java26
-rw-r--r--db/java/src/com/sleepycat/collections/MapEntryParameter.java126
-rw-r--r--db/java/src/com/sleepycat/collections/PrimaryKeyAssigner.java30
-rw-r--r--db/java/src/com/sleepycat/collections/RangeCursor.java874
-rw-r--r--db/java/src/com/sleepycat/collections/StoredCollection.java460
-rw-r--r--db/java/src/com/sleepycat/collections/StoredCollections.java156
-rw-r--r--db/java/src/com/sleepycat/collections/StoredContainer.java415
-rw-r--r--db/java/src/com/sleepycat/collections/StoredEntrySet.java176
-rw-r--r--db/java/src/com/sleepycat/collections/StoredIterator.java600
-rw-r--r--db/java/src/com/sleepycat/collections/StoredKeySet.java144
-rw-r--r--db/java/src/com/sleepycat/collections/StoredList.java604
-rw-r--r--db/java/src/com/sleepycat/collections/StoredMap.java511
-rw-r--r--db/java/src/com/sleepycat/collections/StoredMapEntry.java41
-rw-r--r--db/java/src/com/sleepycat/collections/StoredSortedEntrySet.java220
-rw-r--r--db/java/src/com/sleepycat/collections/StoredSortedKeySet.java241
-rw-r--r--db/java/src/com/sleepycat/collections/StoredSortedMap.java348
-rw-r--r--db/java/src/com/sleepycat/collections/StoredSortedValueSet.java255
-rw-r--r--db/java/src/com/sleepycat/collections/StoredValueSet.java220
-rw-r--r--db/java/src/com/sleepycat/collections/TransactionRunner.java221
-rw-r--r--db/java/src/com/sleepycat/collections/TransactionWorker.java28
-rw-r--r--db/java/src/com/sleepycat/collections/TupleSerialFactory.java135
-rw-r--r--db/java/src/com/sleepycat/collections/package.html21
-rw-r--r--db/java/src/com/sleepycat/compat/DbCompat.java255
-rw-r--r--db/java/src/com/sleepycat/db/BtreePrefixCalculator.java14
-rw-r--r--db/java/src/com/sleepycat/db/BtreeStats.java146
-rw-r--r--db/java/src/com/sleepycat/db/CacheFile.java70
-rw-r--r--db/java/src/com/sleepycat/db/CacheFilePriority.java61
-rw-r--r--db/java/src/com/sleepycat/db/CacheFileStats.java68
-rw-r--r--db/java/src/com/sleepycat/db/CacheStats.java224
-rw-r--r--db/java/src/com/sleepycat/db/CheckpointConfig.java60
-rw-r--r--db/java/src/com/sleepycat/db/Cursor.java349
-rw-r--r--db/java/src/com/sleepycat/db/CursorConfig.java76
-rw-r--r--db/java/src/com/sleepycat/db/Database.java314
-rw-r--r--db/java/src/com/sleepycat/db/DatabaseConfig.java628
-rw-r--r--db/java/src/com/sleepycat/db/DatabaseEntry.java181
-rw-r--r--db/java/src/com/sleepycat/db/DatabaseException.java54
-rw-r--r--db/java/src/com/sleepycat/db/DatabaseStats.java15
-rw-r--r--db/java/src/com/sleepycat/db/DatabaseType.java65
-rw-r--r--db/java/src/com/sleepycat/db/DeadlockException.java20
-rw-r--r--db/java/src/com/sleepycat/db/Environment.java354
-rw-r--r--db/java/src/com/sleepycat/db/EnvironmentConfig.java1076
-rw-r--r--db/java/src/com/sleepycat/db/ErrorHandler.java14
-rw-r--r--db/java/src/com/sleepycat/db/FeedbackHandler.java16
-rw-r--r--db/java/src/com/sleepycat/db/HashStats.java116
-rw-r--r--db/java/src/com/sleepycat/db/Hasher.java14
-rw-r--r--db/java/src/com/sleepycat/db/JoinConfig.java39
-rw-r--r--db/java/src/com/sleepycat/db/JoinCursor.java60
-rw-r--r--db/java/src/com/sleepycat/db/KeyRange.java16
-rw-r--r--db/java/src/com/sleepycat/db/Lock.java31
-rw-r--r--db/java/src/com/sleepycat/db/LockDetectMode.java90
-rw-r--r--db/java/src/com/sleepycat/db/LockMode.java40
-rw-r--r--db/java/src/com/sleepycat/db/LockNotGrantedException.java57
-rw-r--r--db/java/src/com/sleepycat/db/LockOperation.java65
-rw-r--r--db/java/src/com/sleepycat/db/LockRequest.java83
-rw-r--r--db/java/src/com/sleepycat/db/LockRequestMode.java43
-rw-r--r--db/java/src/com/sleepycat/db/LockStats.java164
-rw-r--r--db/java/src/com/sleepycat/db/LogCursor.java80
-rw-r--r--db/java/src/com/sleepycat/db/LogRecordHandler.java17
-rw-r--r--db/java/src/com/sleepycat/db/LogSequenceNumber.java38
-rw-r--r--db/java/src/com/sleepycat/db/LogStats.java146
-rw-r--r--db/java/src/com/sleepycat/db/MemoryException.java41
-rw-r--r--db/java/src/com/sleepycat/db/MessageHandler.java14
-rw-r--r--db/java/src/com/sleepycat/db/MultipleDataEntry.java57
-rw-r--r--db/java/src/com/sleepycat/db/MultipleEntry.java28
-rw-r--r--db/java/src/com/sleepycat/db/MultipleKeyDataEntry.java63
-rw-r--r--db/java/src/com/sleepycat/db/MultipleRecnoDataEntry.java61
-rw-r--r--db/java/src/com/sleepycat/db/OperationStatus.java54
-rw-r--r--db/java/src/com/sleepycat/db/PanicHandler.java14
-rw-r--r--db/java/src/com/sleepycat/db/PreparedTransaction.java30
-rw-r--r--db/java/src/com/sleepycat/db/QueueStats.java98
-rw-r--r--db/java/src/com/sleepycat/db/RecordNumberAppender.java15
-rw-r--r--db/java/src/com/sleepycat/db/RecoveryOperation.java56
-rw-r--r--db/java/src/com/sleepycat/db/ReplicationHandleDeadException.java20
-rw-r--r--db/java/src/com/sleepycat/db/ReplicationStats.java278
-rw-r--r--db/java/src/com/sleepycat/db/ReplicationStatus.java121
-rw-r--r--db/java/src/com/sleepycat/db/ReplicationTransport.java26
-rw-r--r--db/java/src/com/sleepycat/db/RunRecoveryException.java20
-rw-r--r--db/java/src/com/sleepycat/db/SecondaryConfig.java91
-rw-r--r--db/java/src/com/sleepycat/db/SecondaryCursor.java250
-rw-r--r--db/java/src/com/sleepycat/db/SecondaryDatabase.java106
-rw-r--r--db/java/src/com/sleepycat/db/SecondaryKeyCreator.java18
-rw-r--r--db/java/src/com/sleepycat/db/Sequence.java63
-rw-r--r--db/java/src/com/sleepycat/db/SequenceConfig.java199
-rw-r--r--db/java/src/com/sleepycat/db/SequenceStats.java74
-rw-r--r--db/java/src/com/sleepycat/db/StatsConfig.java56
-rw-r--r--db/java/src/com/sleepycat/db/Transaction.java75
-rw-r--r--db/java/src/com/sleepycat/db/TransactionConfig.java89
-rw-r--r--db/java/src/com/sleepycat/db/TransactionStats.java147
-rw-r--r--db/java/src/com/sleepycat/db/VerifyConfig.java81
-rw-r--r--db/java/src/com/sleepycat/db/internal/Db.java399
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbClient.java17
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbConstants.java182
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbEnv.java434
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbLock.java51
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbLogc.java54
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbMpoolFile.java56
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbSequence.java96
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbTxn.java103
-rw-r--r--db/java/src/com/sleepycat/db/internal/DbUtil.java179
-rw-r--r--db/java/src/com/sleepycat/db/internal/Dbc.java73
-rw-r--r--db/java/src/com/sleepycat/db/internal/db_java.java34
-rw-r--r--db/java/src/com/sleepycat/db/internal/db_javaJNI.java263
-rw-r--r--db/java/src/com/sleepycat/db/package.html28
-rw-r--r--db/java/src/com/sleepycat/util/ExceptionUnwrapper.java69
-rw-r--r--db/java/src/com/sleepycat/util/ExceptionWrapper.java25
-rw-r--r--db/java/src/com/sleepycat/util/FastInputStream.java179
-rw-r--r--db/java/src/com/sleepycat/util/FastOutputStream.java278
-rw-r--r--db/java/src/com/sleepycat/util/IOExceptionWrapper.java34
-rw-r--r--db/java/src/com/sleepycat/util/RuntimeExceptionWrapper.java32
-rw-r--r--db/java/src/com/sleepycat/util/UtfOps.java281
-rw-r--r--db/java/src/com/sleepycat/util/package.html6
153 files changed, 22213 insertions, 3 deletions
diff --git a/db/java/src/com/sleepycat/bind/ByteArrayBinding.java b/db/java/src/com/sleepycat/bind/ByteArrayBinding.java
new file mode 100644
index 000000000..e684c1fea
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/ByteArrayBinding.java
@@ -0,0 +1,43 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ByteArrayBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A pass-through <code>EntryBinding</code> that uses the entry's byte array as
+ * the key or data object.
+ *
+ * @author Mark Hayes
+ */
+public class ByteArrayBinding implements EntryBinding {
+
+ /**
+ * Creates a byte array binding.
+ */
+ public ByteArrayBinding() {
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(DatabaseEntry entry) {
+
+ byte[] bytes = new byte[entry.getSize()];
+ System.arraycopy(entry.getData(), entry.getOffset(),
+ bytes, 0, bytes.length);
+ return bytes;
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ byte[] bytes = (byte[]) object;
+ entry.setData(bytes, 0, bytes.length);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/EntityBinding.java b/db/java/src/com/sleepycat/bind/EntityBinding.java
new file mode 100644
index 000000000..5209af56a
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/EntityBinding.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: EntityBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A binding between a key-value entry pair and an entity object.
+ *
+ * @author Mark Hayes
+ */
+public interface EntityBinding {
+
+ /**
+ * Converts key and data entry buffers into an entity Object.
+ *
+ * @param key is the source key entry.
+ *
+ * @param data is the source data entry.
+ *
+ * @return the resulting Object.
+ */
+ Object entryToObject(DatabaseEntry key, DatabaseEntry data);
+
+ /**
+ * Extracts the key entry from an entity Object.
+ *
+ * @param object is the source Object.
+ *
+ * @param key is the destination entry buffer.
+ */
+ void objectToKey(Object object, DatabaseEntry key);
+
+ /**
+ * Extracts the data entry from an entity Object.
+ *
+ * @param object is the source Object.
+ *
+ * @param data is the destination entry buffer.
+ */
+ void objectToData(Object object, DatabaseEntry data);
+}
diff --git a/db/java/src/com/sleepycat/bind/EntryBinding.java b/db/java/src/com/sleepycat/bind/EntryBinding.java
new file mode 100644
index 000000000..e7ad56fa7
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/EntryBinding.java
@@ -0,0 +1,38 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: EntryBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A binding between a key or data entry and a key or data object.
+ *
+ * @author Mark Hayes
+ */
+public interface EntryBinding {
+
+ /**
+ * Converts a entry buffer into an Object.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting Object.
+ */
+ Object entryToObject(DatabaseEntry entry);
+
+ /**
+ * Converts an Object into a entry buffer.
+ *
+ * @param object is the source Object.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ void objectToEntry(Object object, DatabaseEntry entry);
+}
diff --git a/db/java/src/com/sleepycat/bind/RecordNumberBinding.java b/db/java/src/com/sleepycat/bind/RecordNumberBinding.java
new file mode 100644
index 000000000..7fe3dce2e
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/RecordNumberBinding.java
@@ -0,0 +1,70 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: RecordNumberBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * An <code>EntryBinding</code> that treats a record number key entry as a
+ * <code>Long</code> key object.
+ *
+ * <p>Record numbers are returned as <code>Long</code> objects, although on
+ * input any <code>Number</code> object may be used.</p>
+ *
+ * @author Mark Hayes
+ */
+public class RecordNumberBinding implements EntryBinding {
+
+ /**
+ * Creates a byte array binding.
+ */
+ public RecordNumberBinding() {
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(DatabaseEntry entry) {
+
+ return new Long(entryToRecordNumber(entry));
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ recordNumberToEntry(((Number) object).longValue(), entry);
+ }
+
+ /**
+ * Utility method for use by bindings to translate a entry buffer to an
+ * record number integer.
+ *
+ * @param entry the entry buffer.
+ *
+ * @return the record number.
+ */
+ public static long entryToRecordNumber(DatabaseEntry entry) {
+
+ return DbCompat.getRecordNumber(entry) & 0xFFFFFFFFL;
+ }
+
+ /**
+ * Utility method for use by bindings to translate a record number integer
+ * to a entry buffer.
+ *
+ * @param recordNumber the record number.
+ *
+ * @param entry the entry buffer to hold the record number.
+ */
+ public static void recordNumberToEntry(long recordNumber,
+ DatabaseEntry entry) {
+ entry.setData(new byte[4], 0, 4);
+ DbCompat.setRecordNumber(entry, (int) recordNumber);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/package.html b/db/java/src/com/sleepycat/bind/package.html
new file mode 100644
index 000000000..cf824682b
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/package.html
@@ -0,0 +1,7 @@
+<!-- $Id: package.html,v 1.1 2004/08/02 18:52:04 mjc Exp $ -->
+<html>
+<body>
+Bindings between database entries and Java objects<br>
+<a href="{@docRoot}/%2e%2e/ref/bdb/cs_bdb_bind%2ehtml" target="_top">[reference guide]</a>.
+</body>
+</html>
diff --git a/db/java/src/com/sleepycat/bind/serial/ClassCatalog.java b/db/java/src/com/sleepycat/bind/serial/ClassCatalog.java
new file mode 100644
index 000000000..5d249b4f8
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/ClassCatalog.java
@@ -0,0 +1,72 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ClassCatalog.java,v 1.3 2004/09/01 14:34:20 mark Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import java.io.ObjectStreamClass;
+
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * A catalog of class description information for use during object
+ * serialization.
+ *
+ * <p>A catalog is used to store class descriptions separately from serialized
+ * objects, to avoid redundantly stored information with each object.
+ * When serialized objects are stored in a database, a {@link
+ * StoredClassCatalog} should be used.</p>
+ *
+ * <p>This information is used for serialization of class descriptors or
+ * java.io.ObjectStreamClass objects, each of which represents a unique class
+ * format. For each unique format, a unique class ID is assigned by the
+ * catalog. The class ID can then be used in the serialization stream in place
+ * of the full class information. When used with {@link SerialInput} and
+ * {@link SerialOutput} or any of the serial bindings, the use of the catalog
+ * is transparent to the application.</p>
+ *
+ * @author Mark Hayes
+ */
+public interface ClassCatalog {
+
+ /**
+ * Close a catalog database and release any cached resources.
+ */
+ public void close()
+ throws DatabaseException;
+
+ /**
+ * Return the class ID for the current version of the given class
+ * description.
+ * This is used for storing in serialization streams in place of a full
+ * class descriptor, since it is much more compact. To get back the
+ * ObjectStreamClass for a class ID, call {@link #getClassFormat(byte[])}.
+ * This function causes a new class ID to be assigned if the class
+ * description has changed.
+ *
+ * @param classDesc The class description for which to return the
+ * class ID.
+ *
+ * @return The class ID for the current version of the class.
+ */
+ public byte[] getClassID(ObjectStreamClass classDesc)
+ throws DatabaseException, ClassNotFoundException;
+
+ /**
+ * Return the ObjectStreamClass for the given class ID. This may or may
+ * not be the current class format, depending on whether the class has
+ * changed since the class ID was generated.
+ *
+ * @param classID The class ID for which to return the class format.
+ *
+ * @return The class format for the given class ID, which may or may not
+ * represent the current version of the class.
+ */
+ public ObjectStreamClass getClassFormat(byte[] classID)
+ throws DatabaseException, ClassNotFoundException;
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/SerialBinding.java b/db/java/src/com/sleepycat/bind/serial/SerialBinding.java
new file mode 100644
index 000000000..e2e607c14
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/SerialBinding.java
@@ -0,0 +1,130 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: SerialBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import java.io.IOException;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.util.FastInputStream;
+import com.sleepycat.util.FastOutputStream;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * A concrete <code>EntryBinding</code> that treats a key or data entry as
+ * a serialized object.
+ *
+ * <p>This binding stores objects in serialized object format. The
+ * deserialized objects are returned by the binding, and their
+ * <code>Class</code> must implement the <code>Serializable</code>
+ * interface.</p>
+ *
+ * @author Mark Hayes
+ */
+public class SerialBinding implements EntryBinding {
+
+ private ClassCatalog classCatalog;
+ private Class baseClass;
+
+ /**
+ * Creates a serial binding.
+ *
+ * @param classCatalog is the catalog to hold shared class information and
+ * for a database should be a {@link StoredClassCatalog}.
+ *
+ * @param baseClass is the base class for serialized objects stored using
+ * this binding -- all objects using this binding must be an instance of
+ * this class.
+ */
+ public SerialBinding(ClassCatalog classCatalog, Class baseClass) {
+
+ if (classCatalog == null) {
+ throw new NullPointerException("classCatalog must be non-null");
+ }
+ this.classCatalog = classCatalog;
+ this.baseClass = baseClass;
+ }
+
+ /**
+ * Returns the base class for this binding.
+ *
+ * @return the base class for this binding.
+ */
+ public final Class getBaseClass() {
+
+ return baseClass;
+ }
+
+ /**
+ * Deserialize an object from an entry buffer. May only be called for data
+ * that was serialized using {@link #objectToEntry}, since the fixed
+ * serialization header is assumed to not be included in the input data.
+ * {@link SerialInput} is used to deserialize the object.
+ *
+ * @param entry is the input serialized entry.
+ *
+ * @return the output deserialized object.
+ */
+ public Object entryToObject(DatabaseEntry entry) {
+
+ int length = entry.getSize();
+ byte[] hdr = SerialOutput.getStreamHeader();
+ byte[] bufWithHeader = new byte[length + hdr.length];
+
+ System.arraycopy(hdr, 0, bufWithHeader, 0, hdr.length);
+ System.arraycopy(entry.getData(), entry.getOffset(),
+ bufWithHeader, hdr.length, length);
+
+ try {
+ SerialInput jin = new SerialInput(
+ new FastInputStream(bufWithHeader, 0, bufWithHeader.length),
+ classCatalog);
+ return jin.readObject();
+ } catch (IOException e) {
+ throw new RuntimeExceptionWrapper(e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+
+ /**
+ * Serialize an object into an entry buffer. The fixed serialization
+ * header is not included in the output data to save space, and therefore
+ * to deserialize the data the complementary {@link #entryToObject} method
+ * must be used. {@link SerialOutput} is used to serialize the object.
+ *
+ * @param object is the input deserialized object.
+ *
+ * @param entry is the output serialized entry.
+ *
+ * @throws IllegalArgumentException if the object is not an instance of the
+ * base class for this binding.
+ */
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ if (baseClass != null && !baseClass.isInstance(object)) {
+ throw new IllegalArgumentException(
+ "Data object class (" + object.getClass() +
+ ") not an instance of binding's base class (" +
+ baseClass + ')');
+ }
+ FastOutputStream fo = new FastOutputStream();
+ try {
+ SerialOutput jos = new SerialOutput(fo, classCatalog);
+ jos.writeObject(object);
+ } catch (IOException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+
+ byte[] hdr = SerialOutput.getStreamHeader();
+ entry.setData(fo.getBufferBytes(), hdr.length,
+ fo.getBufferLength() - hdr.length);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/SerialInput.java b/db/java/src/com/sleepycat/bind/serial/SerialInput.java
new file mode 100644
index 000000000..16bfd859f
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/SerialInput.java
@@ -0,0 +1,75 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: SerialInput.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * A specialized <code>ObjectInputStream</code> that gets class description
+ * information from a <code>ClassCatalog</code>. It is used by
+ * <code>SerialBinding</code>.
+ *
+ * <p>This class is used instead of an {@link ObjectInputStream}, which it
+ * extends, to read an object stream written by the {@link SerialOutput} class.
+ * For reading objects from a database normally one of the serial binding
+ * classes is used. {@link SerialInput} is used when an {@link
+ * ObjectInputStream} is needed along with compact storage. A {@link
+ * ClassCatalog} must be supplied, however, to stored shared class
+ * descriptions.</p>
+ *
+ * @author Mark Hayes
+ */
+public class SerialInput extends ObjectInputStream {
+
+ private ClassCatalog classCatalog;
+
+ /**
+ * Creates a serial input stream.
+ *
+ * @param in is the input stream from which compact serialized objects will
+ * be read.
+ *
+ * @param classCatalog is the catalog containing the class descriptions
+ * for the serialized objects.
+ */
+ public SerialInput(InputStream in, ClassCatalog classCatalog)
+ throws IOException {
+
+ super(in);
+
+ this.classCatalog = classCatalog;
+ }
+
+ // javadoc is inherited
+ protected ObjectStreamClass readClassDescriptor()
+ throws IOException, ClassNotFoundException {
+
+ try {
+ byte len = readByte();
+ byte[] id = new byte[len];
+ readFully(id);
+
+ return classCatalog.getClassFormat(id);
+ } catch (DatabaseException e) {
+ /*
+ * Do not throw IOException from here since ObjectOutputStream
+ * will write the exception to the stream, which causes another
+ * call here, etc.
+ */
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/SerialOutput.java b/db/java/src/com/sleepycat/bind/serial/SerialOutput.java
new file mode 100644
index 000000000..22ae8f872
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/SerialOutput.java
@@ -0,0 +1,114 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: SerialOutput.java,v 1.3 2004/09/01 14:34:20 mark Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.ObjectStreamConstants;
+import java.io.OutputStream;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * A specialized <code>ObjectOutputStream</code> that stores class description
+ * information in a <code>ClassCatalog</code>. It is used by
+ * <code>SerialBinding</code>.
+ *
+ * <p>This class is used instead of an {@link ObjectOutputStream}, which it
+ * extends, to write a compact object stream. For writing objects to a
+ * database normally one of the serial binding classes is used. {@link
+ * SerialOutput} is used when an {@link ObjectOutputStream} is needed along
+ * with compact storage. A {@link ClassCatalog} must be supplied, however, to
+ * stored shared class descriptions.</p>
+ *
+ * <p>The {@link ClassCatalog} is used to store class definitions rather than
+ * embedding these into the stream. Instead, a class format identifier is
+ * embedded into the stream. This identifier is then used by {@link
+ * SerialInput} to load the class format to deserialize the object.</p>
+ *
+ * @author Mark Hayes
+ */
+public class SerialOutput extends ObjectOutputStream {
+
+ /* Serialization version constants. Instead of hardcoding these
+ * we get them by creating a SerialOutput, which itself
+ * guarantees that we'll always use a PROTOCOL_VERSION_2 header.
+ */
+ private final static byte[] STREAM_HEADER;
+ static {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ SerialOutput oos = new SerialOutput(baos, null);
+ } catch (IOException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ STREAM_HEADER = baos.toByteArray();
+ }
+
+ private ClassCatalog classCatalog;
+
+ /**
+ * Creates a serial output stream.
+ *
+ * @param out is the output stream to which the compact serialized objects
+ * will be written.
+ *
+ * @param classCatalog is the catalog to which the class descriptions for
+ * the serialized objects will be written.
+ */
+ public SerialOutput(OutputStream out, ClassCatalog classCatalog)
+ throws IOException {
+
+ super(out);
+ this.classCatalog = classCatalog;
+
+ /* guarantee that we'll always use the same serialization format */
+
+ useProtocolVersion(ObjectStreamConstants.PROTOCOL_VERSION_2);
+ }
+
+ // javadoc is inherited
+ protected void writeClassDescriptor(ObjectStreamClass classdesc)
+ throws IOException {
+
+ try {
+ byte[] id = classCatalog.getClassID(classdesc);
+ writeByte(id.length);
+ write(id);
+ } catch (DatabaseException e) {
+ /*
+ * Do not throw IOException from here since ObjectOutputStream
+ * will write the exception to the stream, which causes another
+ * call here, etc.
+ */
+ throw new RuntimeExceptionWrapper(e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+
+ /**
+ * Returns the fixed stream header used for all serialized streams in
+ * PROTOCOL_VERSION_2 format. To save space this header can be removed and
+ * serialized streams before storage and inserted before deserializing.
+ * {@link SerialOutput} always uses PROTOCOL_VERSION_2 serialization format
+ * to guarantee that this header is fixed. {@link SerialBinding} removes
+ * this header from serialized streams automatically.
+ *
+ * @return the fixed stream header.
+ */
+ public static byte[] getStreamHeader() {
+
+ return STREAM_HEADER;
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/SerialSerialBinding.java b/db/java/src/com/sleepycat/bind/serial/SerialSerialBinding.java
new file mode 100644
index 000000000..3fba700bd
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/SerialSerialBinding.java
@@ -0,0 +1,117 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: SerialSerialBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * An abstract <code>EntityBinding</code> that treats an entity's key entry and
+ * data entry as serialized objects.
+ *
+ * <p>This class takes care of serializing and deserializing the key and
+ * data entry automatically. Its three abstract methods must be implemented by
+ * a concrete subclass to convert the deserialized objects to/from an entity
+ * object.</p>
+ * <ul>
+ * <li> {@link #entryToObject(Object,Object)} </li>
+ * <li> {@link #objectToKey(Object)} </li>
+ * <li> {@link #objectToData(Object)} </li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public abstract class SerialSerialBinding implements EntityBinding {
+
+ private SerialBinding keyBinding;
+ private SerialBinding dataBinding;
+
+ /**
+ * Creates a serial-serial entity binding.
+ *
+ * @param classCatalog is the catalog to hold shared class information and
+ * for a database should be a {@link StoredClassCatalog}.
+ *
+ * @param keyClass is the key base class.
+ *
+ * @param dataClass is the data base class.
+ */
+ public SerialSerialBinding(ClassCatalog classCatalog,
+ Class keyClass,
+ Class dataClass) {
+
+ this(new SerialBinding(classCatalog, keyClass),
+ new SerialBinding(classCatalog, dataClass));
+ }
+
+ /**
+ * Creates a serial-serial entity binding.
+ *
+ * @param keyBinding is the key binding.
+ *
+ * @param dataBinding is the data binding.
+ */
+ public SerialSerialBinding(SerialBinding keyBinding,
+ SerialBinding dataBinding) {
+
+ this.keyBinding = keyBinding;
+ this.dataBinding = dataBinding;
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(DatabaseEntry key, DatabaseEntry data) {
+
+ return entryToObject(keyBinding.entryToObject(key),
+ dataBinding.entryToObject(data));
+ }
+
+ // javadoc is inherited
+ public void objectToKey(Object object, DatabaseEntry key) {
+
+ object = objectToKey(object);
+ keyBinding.objectToEntry(object, key);
+ }
+
+ // javadoc is inherited
+ public void objectToData(Object object, DatabaseEntry data) {
+
+ object = objectToData(object);
+ dataBinding.objectToEntry(object, data);
+ }
+
+ /**
+ * Constructs an entity object from deserialized key and data objects.
+ *
+ * @param keyInput is the deserialized key object.
+ *
+ * @param dataInput is the deserialized data object.
+ *
+ * @return the entity object constructed from the key and data.
+ */
+ public abstract Object entryToObject(Object keyInput, Object dataInput);
+
+ /**
+ * Extracts a key object from an entity object.
+ *
+ * @param object is the entity object.
+ *
+ * @return the deserialized key object.
+ */
+ public abstract Object objectToKey(Object object);
+
+ /**
+ * Extracts a data object from an entity object.
+ *
+ * @param object is the entity object.
+ *
+ * @return the deserialized data object.
+ */
+ public abstract Object objectToData(Object object);
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/SerialSerialKeyCreator.java b/db/java/src/com/sleepycat/bind/serial/SerialSerialKeyCreator.java
new file mode 100644
index 000000000..eae756f61
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/SerialSerialKeyCreator.java
@@ -0,0 +1,143 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: SerialSerialKeyCreator.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.db.SecondaryKeyCreator;
+
+/**
+ * A abstract key creator that uses a serial key and a serial data entry.
+ * This class takes care of serializing and deserializing the key and data
+ * entry automatically.
+ * The following abstract method must be implemented by a concrete subclass
+ * to create the index key using these objects
+ * <ul>
+ * <li> {@link #createSecondaryKey(Object,Object)} </li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public abstract class SerialSerialKeyCreator
+ implements SecondaryKeyCreator {
+
+ protected SerialBinding primaryKeyBinding;
+ protected SerialBinding dataBinding;
+ protected SerialBinding indexKeyBinding;
+
+ /**
+ * Creates a serial-serial key creator.
+ *
+ * @param classCatalog is the catalog to hold shared class information and
+ * for a database should be a {@link StoredClassCatalog}.
+ *
+ * @param primaryKeyClass is the primary key base class.
+ *
+ * @param dataClass is the data base class.
+ *
+ * @param indexKeyClass is the index key base class.
+ */
+ public SerialSerialKeyCreator(ClassCatalog classCatalog,
+ Class primaryKeyClass,
+ Class dataClass,
+ Class indexKeyClass) {
+
+ this(new SerialBinding(classCatalog, primaryKeyClass),
+ new SerialBinding(classCatalog, dataClass),
+ new SerialBinding(classCatalog, indexKeyClass));
+ }
+
+ /**
+ * Creates a serial-serial entity binding.
+ *
+ * @param primaryKeyBinding is the primary key binding.
+ *
+ * @param dataBinding is the data binding.
+ *
+ * @param indexKeyBinding is the index key binding.
+ */
+ public SerialSerialKeyCreator(SerialBinding primaryKeyBinding,
+ SerialBinding dataBinding,
+ SerialBinding indexKeyBinding) {
+
+ this.primaryKeyBinding = primaryKeyBinding;
+ this.dataBinding = dataBinding;
+ this.indexKeyBinding = indexKeyBinding;
+ }
+
+ // javadoc is inherited
+ public boolean createSecondaryKey(SecondaryDatabase db,
+ DatabaseEntry primaryKeyEntry,
+ DatabaseEntry dataEntry,
+ DatabaseEntry indexKeyEntry)
+ throws DatabaseException {
+
+ Object primaryKeyInput =
+ primaryKeyBinding.entryToObject(primaryKeyEntry);
+ Object dataInput = dataBinding.entryToObject(dataEntry);
+ Object indexKey = createSecondaryKey(primaryKeyInput, dataInput);
+ if (indexKey != null) {
+ indexKeyBinding.objectToEntry(indexKey, indexKeyEntry);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // javadoc is inherited
+ public boolean nullifyForeignKey(SecondaryDatabase db,
+ DatabaseEntry dataEntry)
+ throws DatabaseException {
+
+ Object data = dataBinding.entryToObject(dataEntry);
+ data = nullifyForeignKey(data);
+ if (data != null) {
+ dataBinding.objectToEntry(data, dataEntry);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Creates the index key object from primary key and entry objects.
+ *
+ * @param primaryKey is the deserialized source primary key entry, or
+ * null if no primary key entry is used to construct the index key.
+ *
+ * @param data is the deserialized source data entry, or null if no
+ * data entry is used to construct the index key.
+ *
+ * @return the destination index key object, or null to indicate that
+ * the key is not present.
+ */
+ public abstract Object createSecondaryKey(Object primaryKey, Object data);
+
+ /**
+ * Clears the index key in a data object.
+ *
+ * <p>On entry the data parameter contains the index key to be cleared. It
+ * should be changed by this method such that {@link #createSecondaryKey}
+ * will return false. Other fields in the data object should remain
+ * unchanged.</p>
+ *
+ * @param data is the source and destination data object.
+ *
+ * @return the destination data object, or null to indicate that the
+ * key is not present and no change is necessary. The data returned may
+ * be the same object passed as the data parameter or a newly created
+ * object.
+ */
+ public Object nullifyForeignKey(Object data) {
+
+ return null;
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/StoredClassCatalog.java b/db/java/src/com/sleepycat/bind/serial/StoredClassCatalog.java
new file mode 100644
index 000000000..04caeae3b
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/StoredClassCatalog.java
@@ -0,0 +1,446 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredClassCatalog.java,v 1.4 2004/09/01 14:34:20 mark Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.HashMap;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.CursorConfig;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.LockMode;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+import com.sleepycat.util.UtfOps;
+
+/**
+ * A <code>ClassCatalog</code> that is stored in a <code>Database</code>.
+ *
+ * <p>A single <code>StoredClassCatalog</code> object is normally used along
+ * with a set of databases that stored serialized objects.</p>
+ *
+ * @author Mark Hayes
+ */
+public class StoredClassCatalog implements ClassCatalog {
+
+ /*
+ * Record types ([key] [data]):
+ *
+ * [0] [next class ID]
+ * [1 / class ID] [ObjectStreamClass (class format)]
+ * [2 / class name] [ClassInfo (has 8 byte class ID)]
+ */
+ private static final byte REC_LAST_CLASS_ID = (byte) 0;
+ private static final byte REC_CLASS_FORMAT = (byte) 1;
+ private static final byte REC_CLASS_INFO = (byte) 2;
+
+ private static final byte[] LAST_CLASS_ID_KEY = {REC_LAST_CLASS_ID};
+
+ private Database db;
+ private HashMap classMap;
+ private HashMap formatMap;
+ private LockMode writeLockMode;
+ private boolean cdbMode;
+ private boolean txnMode;
+
+ /**
+ * Creates a catalog based on a given database. To save resources, only a
+ * single catalog object should be used for each unique catalog database.
+ *
+ * @param database an open database to use as the class catalog. It must
+ * be a BTREE database and must not allow duplicates.
+ *
+ * @throws DatabaseException if an error occurs accessing the database.
+ *
+ * @throws IllegalArgumentException if the database is not a BTREE database
+ * or if it configured to allow duplicates.
+ */
+ public StoredClassCatalog(Database database)
+ throws DatabaseException, IllegalArgumentException {
+
+ db = database;
+ DatabaseConfig dbConfig = db.getConfig();
+ EnvironmentConfig envConfig = db.getEnvironment().getConfig();
+
+ writeLockMode = (DbCompat.getInitializeLocking(envConfig) ||
+ envConfig.getTransactional()) ? LockMode.RMW
+ : LockMode.DEFAULT;
+ cdbMode = DbCompat.getInitializeCDB(envConfig);
+ txnMode = dbConfig.getTransactional();
+
+ if (!DbCompat.isTypeBtree(dbConfig)) {
+ throw new IllegalArgumentException(
+ "The class catalog must be a BTREE database.");
+ }
+ if (DbCompat.getSortedDuplicates(dbConfig) ||
+ DbCompat.getUnsortedDuplicates(dbConfig)) {
+ throw new IllegalArgumentException(
+ "The class catalog database must not allow duplicates.");
+ }
+
+ /*
+ * Create the class format and class info maps. Note that these are not
+ * synchronized, and therefore the methods that use them are
+ * synchronized.
+ */
+ classMap = new HashMap();
+ formatMap = new HashMap();
+
+ /*
+ * To avoid phantoms, use putNoOverwrite to ensure that there is always
+ * a class ID record.
+ */
+ if (!dbConfig.getReadOnly()) {
+ DatabaseEntry key = new DatabaseEntry(LAST_CLASS_ID_KEY);
+ DatabaseEntry data = new DatabaseEntry(new byte[1]); // zero ID
+ db.putNoOverwrite(null, key, data);
+ }
+ }
+
+ // javadoc is inherited
+ public synchronized void close()
+ throws DatabaseException {
+
+ if (db != null) {
+ db.close();
+ }
+ db = null;
+ formatMap = null;
+ classMap = null;
+ }
+
+ // javadoc is inherited
+ public synchronized byte[] getClassID(ObjectStreamClass classFormat)
+ throws DatabaseException, ClassNotFoundException {
+
+ ClassInfo classInfo = getClassInfo(classFormat);
+ return classInfo.getClassID();
+ }
+
+ // javadoc is inherited
+ public synchronized ObjectStreamClass getClassFormat(byte[] classID)
+ throws DatabaseException, ClassNotFoundException {
+
+ return getClassFormat(classID, new DatabaseEntry());
+ }
+
+ /**
+ * Internal function for getting the class format. Allows passing the
+ * DatabaseEntry object for the data, so the bytes of the class format can
+ * be examined afterwards.
+ */
+ private synchronized ObjectStreamClass getClassFormat(byte[] classID,
+ DatabaseEntry data)
+ throws DatabaseException, ClassNotFoundException {
+
+ /* First check the map and, if found, add class info to the map. */
+
+ BigInteger classIDObj = new BigInteger(classID);
+ ObjectStreamClass classFormat =
+ (ObjectStreamClass) formatMap.get(classIDObj);
+ if (classFormat == null) {
+
+ /* Make the class format key. */
+
+ byte[] keyBytes = new byte[classID.length + 1];
+ keyBytes[0] = REC_CLASS_FORMAT;
+ System.arraycopy(classID, 0, keyBytes, 1, classID.length);
+ DatabaseEntry key = new DatabaseEntry(keyBytes);
+
+ /* Read the class format. */
+
+ OperationStatus status = db.get(null, key, data, LockMode.DEFAULT);
+ if (status != OperationStatus.SUCCESS) {
+ throw new ClassNotFoundException("Catalog class ID not found");
+ }
+ try {
+ ObjectInputStream ois =
+ new ObjectInputStream(
+ new ByteArrayInputStream(data.getData(),
+ data.getOffset(),
+ data.getSize()));
+ classFormat = (ObjectStreamClass) ois.readObject();
+ } catch (IOException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+
+ /* Update the class format map. */
+
+ formatMap.put(classIDObj, classFormat);
+ }
+ return classFormat;
+ }
+
+ /**
+ * Get the ClassInfo for a given class name, adding it and its
+ * ObjectStreamClass to the database if they are not already present, and
+ * caching both of them using the class info and class format maps. When a
+ * class is first loaded from the database, the stored ObjectStreamClass is
+ * compared to the current ObjectStreamClass loaded by the Java class
+ * loader; if they are different, a new class ID is assigned for the
+ * current format.
+ */
+ private synchronized ClassInfo getClassInfo(ObjectStreamClass classFormat)
+ throws DatabaseException, ClassNotFoundException {
+
+ /*
+ * First check for a cached copy of the class info, which if
+ * present always contains the class format object
+ */
+ String className = classFormat.getName();
+ ClassInfo classInfo = (ClassInfo) classMap.get(className);
+ if (classInfo != null) {
+ return classInfo;
+ } else {
+ /* Make class info key. */
+ char[] nameChars = className.toCharArray();
+ byte[] keyBytes = new byte[1 + UtfOps.getByteLength(nameChars)];
+ keyBytes[0] = REC_CLASS_INFO;
+ UtfOps.charsToBytes(nameChars, 0, keyBytes, 1, nameChars.length);
+ DatabaseEntry key = new DatabaseEntry(keyBytes);
+
+ /* Read class info. */
+ DatabaseEntry data = new DatabaseEntry();
+ OperationStatus status = db.get(null, key, data, LockMode.DEFAULT);
+ if (status != OperationStatus.SUCCESS) {
+ /*
+ * Not found in the database, write class info and class
+ * format.
+ */
+ classInfo = putClassInfo(new ClassInfo(), className, key,
+ classFormat);
+ } else {
+ /*
+ * Read class info to get the class format key, then read class
+ * format.
+ */
+ classInfo = new ClassInfo(data);
+ DatabaseEntry formatData = new DatabaseEntry();
+ ObjectStreamClass storedClassFormat =
+ getClassFormat(classInfo.getClassID(), formatData);
+
+ /*
+ * Compare the stored class format to the current class format,
+ * and if they are different then generate a new class ID.
+ */
+ if (!areClassFormatsEqual(storedClassFormat,
+ getBytes(formatData),
+ classFormat)) {
+ classInfo = putClassInfo(classInfo, className, key,
+ classFormat);
+ }
+
+ /* Update the class info map. */
+ classInfo.setClassFormat(classFormat);
+ classMap.put(className, classInfo);
+ }
+ }
+ return classInfo;
+ }
+
+ /**
+ * Assign a new class ID (increment the current ID record), write the
+ * ObjectStreamClass record for this new ID, and update the ClassInfo
+ * record with the new ID also. The ClassInfo passed as an argument is the
+ * one to be updated.
+ */
+ private synchronized ClassInfo putClassInfo(ClassInfo classInfo,
+ String className,
+ DatabaseEntry classKey,
+ ObjectStreamClass classFormat)
+ throws DatabaseException, ClassNotFoundException {
+
+ /* An intent-to-write cursor is needed for CDB. */
+ CursorConfig cursorConfig = null;
+ if (cdbMode) {
+ cursorConfig = new CursorConfig();
+ DbCompat.setWriteCursor(cursorConfig, true);
+ }
+ Cursor cursor = null;
+ Transaction txn = null;
+ try {
+ if (txnMode) {
+ txn = db.getEnvironment().beginTransaction(null, null);
+ }
+ cursor = db.openCursor(txn, cursorConfig);
+
+ /* Get the current class ID. */
+ DatabaseEntry key = new DatabaseEntry(LAST_CLASS_ID_KEY);
+ DatabaseEntry data = new DatabaseEntry();
+ OperationStatus status = cursor.getSearchKey(key, data,
+ writeLockMode);
+ if (status != OperationStatus.SUCCESS) {
+ throw new IllegalStateException("Class ID not initialized");
+ }
+ byte[] idBytes = getBytes(data);
+
+ /* Increment the ID by one and write the updated record. */
+ idBytes = incrementID(idBytes);
+ data.setData(idBytes);
+ cursor.put(key, data);
+
+ /*
+ * Write the new class format record whose key is the ID just
+ * assigned.
+ */
+ byte[] keyBytes = new byte[1 + idBytes.length];
+ keyBytes[0] = REC_CLASS_FORMAT;
+ System.arraycopy(idBytes, 0, keyBytes, 1, idBytes.length);
+ key.setData(keyBytes);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos;
+ try {
+ oos = new ObjectOutputStream(baos);
+ oos.writeObject(classFormat);
+ } catch (IOException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ data.setData(baos.toByteArray());
+
+ cursor.put(key, data);
+
+ /*
+ * Write the new class info record, using the key passed in; this
+ * is done last so that a reader who gets the class info record
+ * first will always find the corresponding class format record.
+ */
+ classInfo.setClassID(idBytes);
+ classInfo.toDbt(data);
+
+ cursor.put(classKey, data);
+
+ /*
+ * Update the maps before closing the cursor, so that the cursor
+ * lock prevents other writers from duplicating this entry.
+ */
+ classInfo.setClassFormat(classFormat);
+ classMap.put(className, classInfo);
+ formatMap.put(new BigInteger(idBytes), classFormat);
+ return classInfo;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ if (txn != null) {
+ txn.commit();
+ }
+ }
+ }
+
+ private static byte[] incrementID(byte[] key) {
+
+ BigInteger id = new BigInteger(key);
+ id = id.add(BigInteger.valueOf(1));
+ return id.toByteArray();
+ }
+
+ /**
+ * Holds the class format key for a class, maintains a reference to the
+ * ObjectStreamClass. Other fields can be added when we need to store more
+ * information per class.
+ */
+ private static class ClassInfo implements Serializable {
+
+ private byte[] classID;
+ private transient ObjectStreamClass classFormat;
+
+ ClassInfo() {
+ }
+
+ ClassInfo(DatabaseEntry dbt) {
+
+ byte[] data = dbt.getData();
+ int len = data[0];
+ classID = new byte[len];
+ System.arraycopy(data, 1, classID, 0, len);
+ }
+
+ void toDbt(DatabaseEntry dbt) {
+
+ byte[] data = new byte[1 + classID.length];
+ data[0] = (byte) classID.length;
+ System.arraycopy(classID, 0, data, 1, classID.length);
+ dbt.setData(data);
+ }
+
+ void setClassID(byte[] classID) {
+
+ this.classID = classID;
+ }
+
+ byte[] getClassID() {
+
+ return classID;
+ }
+
+ ObjectStreamClass getClassFormat() {
+
+ return classFormat;
+ }
+
+ void setClassFormat(ObjectStreamClass classFormat) {
+
+ this.classFormat = classFormat;
+ }
+ }
+
+ /**
+ * Return whether two class formats are equal. This determines whether a
+ * new class format is needed for an object being serialized. Formats must
+ * be identical in all respects, or a new format is needed.
+ */
+ private static boolean areClassFormatsEqual(ObjectStreamClass format1,
+ byte[] format1Bytes,
+ ObjectStreamClass format2) {
+ try {
+ if (format1Bytes == null) { // using cached format1 object
+ format1Bytes = getObjectBytes(format1);
+ }
+ byte[] format2Bytes = getObjectBytes(format2);
+ return java.util.Arrays.equals(format2Bytes, format1Bytes);
+ } catch (IOException e) { return false; }
+ }
+
+ private static byte[] getBytes(DatabaseEntry dbt) {
+ byte[] b = dbt.getData();
+ if (b == null) {
+ return null;
+ }
+ if (dbt.getOffset() == 0 && b.length == dbt.getSize()) {
+ return b;
+ }
+ byte[] t = new byte[dbt.getSize()];
+ System.arraycopy(b, dbt.getOffset(), t, 0, t.length);
+ return t;
+ }
+
+ private static byte[] getObjectBytes(Object o)
+ throws IOException {
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(o);
+ return baos.toByteArray();
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/TupleSerialBinding.java b/db/java/src/com/sleepycat/bind/serial/TupleSerialBinding.java
new file mode 100644
index 000000000..2e30c3f5f
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/TupleSerialBinding.java
@@ -0,0 +1,115 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleSerialBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * An abstract <code>EntityBinding</code> that treats an entity's key entry as
+ * a tuple and its data entry as a serialized object.
+ *
+ * <p>This class takes care of serializing and deserializing the data entry,
+ * and converting the key entry to/from {@link TupleInput} and {@link
+ * TupleOutput} objects. Its three abstract methods must be implemented by a
+ * concrete subclass to convert these objects to/from an entity object.</p>
+ * <ul>
+ * <li> {@link #entryToObject(TupleInput,Object)} </li>
+ * <li> {@link #objectToKey(Object,TupleOutput)} </li>
+ * <li> {@link #objectToData(Object)} </li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public abstract class TupleSerialBinding implements EntityBinding {
+
+ protected SerialBinding dataBinding;
+
+ /**
+ * Creates a tuple-serial entity binding.
+ *
+ * @param classCatalog is the catalog to hold shared class information and
+ * for a database should be a {@link StoredClassCatalog}.
+ *
+ * @param baseClass is the base class.
+ */
+ public TupleSerialBinding(ClassCatalog classCatalog,
+ Class baseClass) {
+
+ this(new SerialBinding(classCatalog, baseClass));
+ }
+
+ /**
+ * Creates a tuple-serial entity binding.
+ *
+ * @param dataBinding is the data binding.
+ */
+ public TupleSerialBinding(SerialBinding dataBinding) {
+
+ this.dataBinding = dataBinding;
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(DatabaseEntry key, DatabaseEntry data) {
+
+ return entryToObject(TupleBinding.entryToInput(key),
+ dataBinding.entryToObject(data));
+ }
+
+ // javadoc is inherited
+ public void objectToKey(Object object, DatabaseEntry key) {
+
+ TupleOutput output = TupleBinding.newOutput();
+ objectToKey(object, output);
+ TupleBinding.outputToEntry(output, key);
+ }
+
+ // javadoc is inherited
+ public void objectToData(Object object, DatabaseEntry data) {
+
+ object = objectToData(object);
+ dataBinding.objectToEntry(object, data);
+ }
+
+ /**
+ * Constructs an entity object from {@link TupleInput} key entry and
+ * deserialized data entry objects.
+ *
+ * @param keyInput is the {@link TupleInput} key entry object.
+ *
+ * @param dataInput is the deserialized data entry object.
+ *
+ * @return the entity object constructed from the key and data.
+ */
+ public abstract Object entryToObject(TupleInput keyInput,
+ Object dataInput);
+
+ /**
+ * Extracts a key tuple from an entity object.
+ *
+ * @param object is the entity object.
+ *
+ * @param keyOutput is the {@link TupleOutput} to which the key should be
+ * written.
+ */
+ public abstract void objectToKey(Object object, TupleOutput keyOutput);
+
+ /**
+ * Extracts a data object from an entity object.
+ *
+ * @param object is the entity object.
+ *
+ * @return the deserialized data object.
+ */
+ public abstract Object objectToData(Object object);
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/TupleSerialKeyCreator.java b/db/java/src/com/sleepycat/bind/serial/TupleSerialKeyCreator.java
new file mode 100644
index 000000000..f81f75a9a
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/TupleSerialKeyCreator.java
@@ -0,0 +1,137 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleSerialKeyCreator.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.db.SecondaryKeyCreator;
+
+/**
+ * A abstract key creator that uses a tuple key and a serial data entry. This
+ * class takes care of serializing and deserializing the data entry, and
+ * converting the key entry to/from {@link TupleInput} and {@link TupleOutput}
+ * objects.
+ * The following abstract method must be implemented by a concrete subclass
+ * to create the index key using these objects
+ * <ul>
+ * <li> {@link #createSecondaryKey(TupleInput,Object,TupleOutput)} </li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public abstract class TupleSerialKeyCreator
+ implements SecondaryKeyCreator {
+
+ protected SerialBinding dataBinding;
+
+ /**
+ * Creates a tuple-serial key creator.
+ *
+ * @param classCatalog is the catalog to hold shared class information and
+ * for a database should be a {@link StoredClassCatalog}.
+ *
+ * @param dataClass is the data base class.
+ */
+ public TupleSerialKeyCreator(ClassCatalog classCatalog, Class dataClass) {
+
+ this(new SerialBinding(classCatalog, dataClass));
+ }
+
+ /**
+ * Creates a tuple-serial key creator.
+ *
+ * @param dataBinding is the data binding.
+ */
+ public TupleSerialKeyCreator(SerialBinding dataBinding) {
+
+ this.dataBinding = dataBinding;
+ }
+
+ // javadoc is inherited
+ public boolean createSecondaryKey(SecondaryDatabase db,
+ DatabaseEntry primaryKeyEntry,
+ DatabaseEntry dataEntry,
+ DatabaseEntry indexKeyEntry)
+ throws DatabaseException {
+
+ TupleOutput output = TupleBinding.newOutput();
+ TupleInput primaryKeyInput =
+ TupleBinding.entryToInput(primaryKeyEntry);
+ Object dataInput = dataBinding.entryToObject(dataEntry);
+ if (createSecondaryKey(primaryKeyInput, dataInput, output)) {
+ TupleBinding.outputToEntry(output, indexKeyEntry);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // javadoc is inherited
+ public boolean nullifyForeignKey(SecondaryDatabase db,
+ DatabaseEntry dataEntry)
+ throws DatabaseException {
+
+ Object data = dataBinding.entryToObject(dataEntry);
+ data = nullifyForeignKey(data);
+ if (data != null) {
+ dataBinding.objectToEntry(data, dataEntry);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Creates the index key entry from primary key tuple entry and
+ * deserialized data entry.
+ *
+ * @param primaryKeyInput is the {@link TupleInput} for the primary key
+ * entry, or null if no primary key entry is used to construct the index
+ * key.
+ *
+ * @param dataInput is the deserialized data entry, or null if no data
+ * entry is used to construct the index key.
+ *
+ * @param indexKeyOutput is the destination index key tuple. For index
+ * keys which are optionally present, no tuple entry should be output to
+ * indicate that the key is not present or null.
+ *
+ * @return true if a key was created, or false to indicate that the key is
+ * not present.
+ */
+ public abstract boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object dataInput,
+ TupleOutput indexKeyOutput);
+
+ /**
+ * Clears the index key in the deserialized data entry.
+ *
+ * <p>On entry the data parameter contains the index key to be cleared. It
+ * should be changed by this method such that {@link #createSecondaryKey}
+ * will return false. Other fields in the data object should remain
+ * unchanged.</p>
+ *
+ * @param data is the source and destination deserialized data
+ * entry.
+ *
+ * @return the destination data object, or null to indicate that the
+ * key is not present and no change is necessary. The data returned may
+ * be the same object passed as the data parameter or a newly created
+ * object.
+ */
+ public Object nullifyForeignKey(Object data) {
+
+ return null;
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledBinding.java b/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledBinding.java
new file mode 100644
index 000000000..85a254e9a
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledBinding.java
@@ -0,0 +1,93 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleSerialMarshalledBinding.java,v 1.3 2004/09/22 18:01:01 bostic Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A concrete <code>TupleSerialBinding</code> that delegates to the
+ * <code>MarshalledTupleKeyEntity</code> interface of the entity class.
+ *
+ * <p>The {@link MarshalledTupleKeyEntity} interface must be implemented by the
+ * entity class to convert between the key/data entry and entity object.</p>
+ *
+ * <p> The binding is "tricky" in that it uses the entity class for both the
+ * stored data entry and the combined entity object. To do this, the entity's
+ * key field(s) are transient and are set by the binding after the data object
+ * has been deserialized. This avoids the use of a "data" class completely.
+ * </p>
+ *
+ * @author Mark Hayes
+ * @see MarshalledTupleKeyEntity
+ */
+public class TupleSerialMarshalledBinding extends TupleSerialBinding {
+
+ /**
+ * Creates a tuple-serial marshalled binding object.
+ *
+ * @param classCatalog is the catalog to hold shared class information and
+ * for a database should be a {@link StoredClassCatalog}.
+ *
+ * @param baseClass is the base class for serialized objects stored using
+ * this binding -- all objects using this binding must be an instance of
+ * this class.
+ */
+ public TupleSerialMarshalledBinding(ClassCatalog classCatalog,
+ Class baseClass) {
+
+ this(new SerialBinding(classCatalog, baseClass));
+ }
+
+ /**
+ * Creates a tuple-serial marshalled binding object.
+ *
+ * @param dataBinding is the binding used for serializing and deserializing
+ * the entity object.
+ */
+ public TupleSerialMarshalledBinding(SerialBinding dataBinding) {
+
+ super(dataBinding);
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput tupleInput, Object javaInput) {
+
+ /* Creates the entity by combining the stored key and data.
+ * This "tricky" binding returns the stored data as the entity, but
+ * first it sets the transient key fields from the stored key.
+ */
+ MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) javaInput;
+
+ if (tupleInput != null) { // may be null if not used by key extractor
+ entity.unmarshalPrimaryKey(tupleInput);
+ }
+ return entity;
+ }
+
+ // javadoc is inherited
+ public void objectToKey(Object object, TupleOutput output) {
+
+ /* Creates the stored key from the entity.
+ */
+ MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) object;
+ entity.marshalPrimaryKey(output);
+ }
+
+ // javadoc is inherited
+ public Object objectToData(Object object) {
+
+ /* Returns the entity as the stored data. There is nothing to do here
+ * since the entity's key fields are transient.
+ */
+ return object;
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledKeyCreator.java b/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledKeyCreator.java
new file mode 100644
index 000000000..98b8fa637
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledKeyCreator.java
@@ -0,0 +1,75 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleSerialMarshalledKeyCreator.java,v 1.2 2004/06/04 18:24:49 mark Exp $
+ */
+
+package com.sleepycat.bind.serial;
+
+import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A concrete key creator that works in conjunction with a {@link
+ * TupleSerialMarshalledBinding}. This key creator works by calling the
+ * methods of the {@link MarshalledTupleKeyEntity} interface to create and
+ * clear the index key fields.
+ *
+ * @author Mark Hayes
+ */
+public class TupleSerialMarshalledKeyCreator extends TupleSerialKeyCreator {
+
+ private TupleSerialMarshalledBinding binding;
+ private String keyName;
+
+ /**
+ * Creates a tuple-serial marshalled key creator.
+ *
+ * @param binding is the binding used for the tuple-serial entity.
+ *
+ * @param keyName is the key name passed to the {@link
+ * MarshalledTupleKeyEntity#marshalSecondaryKey} method to identify the
+ * index key.
+ */
+ public TupleSerialMarshalledKeyCreator(TupleSerialMarshalledBinding
+ binding,
+ String keyName) {
+
+ super(binding.dataBinding);
+ this.binding = binding;
+ this.keyName = keyName;
+
+ if (dataBinding == null) {
+ throw new NullPointerException("dataBinding may not be null");
+ }
+ }
+
+ // javadoc is inherited
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object dataInput,
+ TupleOutput indexKeyOutput) {
+
+ /*
+ * The primary key is unmarshalled before marshalling the index key, to
+ * account for cases where the index key includes fields taken from the
+ * primary key.
+ */
+ MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity)
+ binding.entryToObject(primaryKeyInput, dataInput);
+
+ return entity.marshalSecondaryKey(keyName, indexKeyOutput);
+ }
+
+ // javadoc is inherited
+ public Object nullifyForeignKey(Object dataInput) {
+
+ MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity)
+ binding.entryToObject(null, dataInput);
+
+ return entity.nullifyForeignKey(keyName) ? dataInput : null;
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/serial/package.html b/db/java/src/com/sleepycat/bind/serial/package.html
new file mode 100644
index 000000000..eab1e2151
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/serial/package.html
@@ -0,0 +1,6 @@
+<!-- $Id: package.html,v 1.1 2004/08/02 18:52:04 mjc Exp $ -->
+<html>
+<body>
+Bindings that use Java serialization.
+</body>
+</html>
diff --git a/db/java/src/com/sleepycat/bind/tuple/BooleanBinding.java b/db/java/src/com/sleepycat/bind/tuple/BooleanBinding.java
new file mode 100644
index 000000000..389b19f25
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/BooleanBinding.java
@@ -0,0 +1,75 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: BooleanBinding.java,v 1.5 2004/08/13 15:16:44 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a <code>Boolean</code> primitive
+ * wrapper or a <code>boolean</code> primitive.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class BooleanBinding extends TupleBinding {
+
+ private static final int BOOLEAN_SIZE = 1;
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ booleanToEntry(((Boolean) object).booleanValue(), entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>boolean</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static boolean entryToBoolean(DatabaseEntry entry) {
+
+ return entryToInput(entry).readBoolean();
+ }
+
+ /**
+ * Converts a simple <code>boolean</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void booleanToEntry(boolean val, DatabaseEntry entry) {
+
+ outputToEntry(newOutput(new byte[BOOLEAN_SIZE]).writeBoolean(val),
+ entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/ByteBinding.java b/db/java/src/com/sleepycat/bind/tuple/ByteBinding.java
new file mode 100644
index 000000000..f227e50c6
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/ByteBinding.java
@@ -0,0 +1,74 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ByteBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a <code>Byte</code> primitive
+ * wrapper or a <code>byte</code> primitive.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class ByteBinding extends TupleBinding {
+
+ private static final int BYTE_SIZE = 1;
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return new Byte(input.readByte());
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ byteToEntry(((Number) object).byteValue(), entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>byte</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static byte entryToByte(DatabaseEntry entry) {
+
+ return entryToInput(entry).readByte();
+ }
+
+ /**
+ * Converts a simple <code>byte</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void byteToEntry(byte val, DatabaseEntry entry) {
+
+ outputToEntry(newOutput(new byte[BYTE_SIZE]).writeByte(val), entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/CharacterBinding.java b/db/java/src/com/sleepycat/bind/tuple/CharacterBinding.java
new file mode 100644
index 000000000..c521a9ac4
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/CharacterBinding.java
@@ -0,0 +1,74 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: CharacterBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a <code>Character</code> primitive
+ * wrapper or a <code>char</code> primitive.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class CharacterBinding extends TupleBinding {
+
+ private static final int CHAR_SIZE = 2;
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return new Character(input.readChar());
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ charToEntry(((Character) object).charValue(), entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>char</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static char entryToChar(DatabaseEntry entry) {
+
+ return entryToInput(entry).readChar();
+ }
+
+ /**
+ * Converts a simple <code>char</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void charToEntry(char val, DatabaseEntry entry) {
+
+ outputToEntry(newOutput(new byte[CHAR_SIZE]).writeChar(val), entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/DoubleBinding.java b/db/java/src/com/sleepycat/bind/tuple/DoubleBinding.java
new file mode 100644
index 000000000..a94c7ecb9
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/DoubleBinding.java
@@ -0,0 +1,75 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DoubleBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a <code>Double</code> primitive
+ * wrapper or a <code>double</code> primitive.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class DoubleBinding extends TupleBinding {
+
+ private static final int DOUBLE_SIZE = 8;
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return new Double(input.readDouble());
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ doubleToEntry(((Number) object).doubleValue(), entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>double</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static double entryToDouble(DatabaseEntry entry) {
+
+ return entryToInput(entry).readDouble();
+ }
+
+ /**
+ * Converts a simple <code>double</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void doubleToEntry(double val, DatabaseEntry entry) {
+
+ outputToEntry(newOutput(new byte[DOUBLE_SIZE]).writeDouble(val),
+ entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/FloatBinding.java b/db/java/src/com/sleepycat/bind/tuple/FloatBinding.java
new file mode 100644
index 000000000..c7d45716f
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/FloatBinding.java
@@ -0,0 +1,74 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: FloatBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a <code>Float</code> primitive
+ * wrapper or a <code>float</code> primitive.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class FloatBinding extends TupleBinding {
+
+ private static final int FLOAT_SIZE = 4;
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return new Float(input.readFloat());
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ floatToEntry(((Number) object).floatValue(), entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>float</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static float entryToFloat(DatabaseEntry entry) {
+
+ return entryToInput(entry).readFloat();
+ }
+
+ /**
+ * Converts a simple <code>float</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void floatToEntry(float val, DatabaseEntry entry) {
+
+ outputToEntry(newOutput(new byte[FLOAT_SIZE]).writeFloat(val), entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/IntegerBinding.java b/db/java/src/com/sleepycat/bind/tuple/IntegerBinding.java
new file mode 100644
index 000000000..c2b6391ee
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/IntegerBinding.java
@@ -0,0 +1,74 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: IntegerBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a <code>Integer</code> primitive
+ * wrapper or an <code>int</code> primitive.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class IntegerBinding extends TupleBinding {
+
+ private static final int INT_SIZE = 4;
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return new Integer(input.readInt());
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ intToEntry(((Number) object).intValue(), entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>int</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static int entryToInt(DatabaseEntry entry) {
+
+ return entryToInput(entry).readInt();
+ }
+
+ /**
+ * Converts a simple <code>int</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void intToEntry(int val, DatabaseEntry entry) {
+
+ outputToEntry(newOutput(new byte[INT_SIZE]).writeInt(val), entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/LongBinding.java b/db/java/src/com/sleepycat/bind/tuple/LongBinding.java
new file mode 100644
index 000000000..6dee013b7
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/LongBinding.java
@@ -0,0 +1,74 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: LongBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a <code>Long</code> primitive
+ * wrapper or a <code>long</code> primitive.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class LongBinding extends TupleBinding {
+
+ private static final int LONG_SIZE = 8;
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return new Long(input.readLong());
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ longToEntry(((Number) object).longValue(), entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>long</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static long entryToLong(DatabaseEntry entry) {
+
+ return entryToInput(entry).readLong();
+ }
+
+ /**
+ * Converts a simple <code>long</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void longToEntry(long val, DatabaseEntry entry) {
+
+ outputToEntry(newOutput(new byte[LONG_SIZE]).writeLong(val), entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleEntry.java b/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleEntry.java
new file mode 100644
index 000000000..9665b3c85
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleEntry.java
@@ -0,0 +1,45 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: MarshalledTupleEntry.java,v 1.2 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+/**
+ * A marshalling interface implemented by key, data or entity classes that
+ * are represented as tuples.
+ *
+ * <p>Key classes implement this interface to marshal their key entry. Data or
+ * entity classes implement this interface to marshal their data entry.
+ * Implementations of this interface must have a public no arguments
+ * constructor so that they can be instantiated by a binding, prior to calling
+ * the {@link #unmarshalEntry} method.</p>
+ *
+ * <p>Note that implementing this interface is not necessary when the object is
+ * a Java simple type, for example: String, Integer, etc. These types can be
+ * used with built-in bindings returned by {@link
+ * TupleBinding#getPrimitiveBinding}.</p>
+ *
+ * @author Mark Hayes
+ * @see TupleTupleMarshalledBinding
+ */
+public interface MarshalledTupleEntry {
+
+ /**
+ * Construct the key or data tuple entry from the key or data object.
+ *
+ * @param dataOutput is the output tuple.
+ */
+ void marshalEntry(TupleOutput dataOutput);
+
+ /**
+ * Construct the key or data object from the key or data tuple entry.
+ *
+ * @param dataInput is the input tuple.
+ */
+ void unmarshalEntry(TupleInput dataInput);
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleKeyEntity.java b/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleKeyEntity.java
new file mode 100644
index 000000000..d7640e9f5
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleKeyEntity.java
@@ -0,0 +1,71 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: MarshalledTupleKeyEntity.java,v 1.3 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+/**
+ * A marshalling interface implemented by entity classes that represent keys as
+ * tuples. Since <code>MarshalledTupleKeyEntity</code> objects are instantiated
+ * using Java deserialization, no particular constructor is required by classes
+ * that implement this interface.
+ *
+ * <p>Note that a marshalled tuple key extractor is somewhat less efficient
+ * than a non-marshalled key tuple extractor because more conversions are
+ * needed. A marshalled key extractor must convert the entry to an object in
+ * order to extract the key fields, while an unmarshalled key extractor does
+ * not.</p>
+ *
+ * @author Mark Hayes
+ * @see TupleTupleMarshalledBinding
+ * @see com.sleepycat.bind.serial.TupleSerialMarshalledBinding
+ */
+public interface MarshalledTupleKeyEntity {
+
+ /**
+ * Extracts the entity's primary key and writes it to the key output.
+ *
+ * @param keyOutput is the output tuple.
+ */
+ void marshalPrimaryKey(TupleOutput keyOutput);
+
+ /**
+ * Completes construction of the entity by setting its primary key from the
+ * stored primary key.
+ *
+ * @param keyInput is the input tuple.
+ */
+ void unmarshalPrimaryKey(TupleInput keyInput);
+
+ /**
+ * Extracts the entity's secondary key and writes it to the key output.
+ *
+ * @param keyName identifies the secondary key.
+ *
+ * @param keyOutput is the output tuple.
+ *
+ * @return true if a key was created, or false to indicate that the key is
+ * not present.
+ */
+ boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput);
+
+ /**
+ * Clears the entity's secondary key fields for the given key name.
+ *
+ * <p>The specified index key should be changed by this method such that
+ * {@link #marshalSecondaryKey} for the same key name will return false.
+ * Other fields in the data object should remain unchanged.</p>
+ *
+ *
+ * @param keyName identifies the secondary key.
+ *
+ * @return true if the key was cleared, or false to indicate that the key
+ * is not present and no change is necessary.
+ */
+ boolean nullifyForeignKey(String keyName);
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/ShortBinding.java b/db/java/src/com/sleepycat/bind/tuple/ShortBinding.java
new file mode 100644
index 000000000..d330f9b1b
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/ShortBinding.java
@@ -0,0 +1,74 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ShortBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a <code>Short</code> primitive
+ * wrapper or a <code>short</code> primitive.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class ShortBinding extends TupleBinding {
+
+ private static final int SHORT_SIZE = 2;
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return new Short(input.readShort());
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ shortToEntry(((Number) object).shortValue(), entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>short</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static short entryToShort(DatabaseEntry entry) {
+
+ return entryToInput(entry).readShort();
+ }
+
+ /**
+ * Converts a simple <code>short</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void shortToEntry(short val, DatabaseEntry entry) {
+
+ outputToEntry(newOutput(new byte[SHORT_SIZE]).writeShort(val), entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/StringBinding.java b/db/java/src/com/sleepycat/bind/tuple/StringBinding.java
new file mode 100644
index 000000000..257e01f44
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/StringBinding.java
@@ -0,0 +1,76 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StringBinding.java,v 1.4 2004/08/02 18:52:05 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.util.UtfOps;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>TupleBinding</code> for a simple <code>String</code> value.
+ *
+ * <p>There are two ways to use this class:</p>
+ * <ol>
+ * <li>When using the {@link com.sleepycat.db} package directly, the static
+ * methods in this class can be used to convert between primitive values and
+ * {@link DatabaseEntry} objects.</li>
+ * <li>When using the {@link com.sleepycat.collections} package, an instance of
+ * this class can be used with any stored collection. The easiest way to
+ * obtain a binding instance is with the {@link
+ * TupleBinding#getPrimitiveBinding} method.</li>
+ * </ol>
+ */
+public class StringBinding extends TupleBinding {
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ return input.readString();
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ stringToEntry((String) object, entry);
+ }
+
+ /**
+ * Converts an entry buffer into a simple <code>String</code> value.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the resulting value.
+ */
+ public static String entryToString(DatabaseEntry entry) {
+
+ return entryToInput(entry).readString();
+ }
+
+ /**
+ * Converts a simple <code>String</code> value into an entry buffer.
+ *
+ * @param val is the source value.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void stringToEntry(String val, DatabaseEntry entry) {
+
+ int stringLength =
+ (val == null) ? 1 : UtfOps.getByteLength(val.toCharArray());
+ stringLength++; // null terminator
+ outputToEntry(newOutput(new byte[stringLength]).writeString(val),
+ entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleBinding.java
new file mode 100644
index 000000000..5ae95545b
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleBinding.java
@@ -0,0 +1,179 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleBinding.java,v 1.4 2004/06/29 06:06:36 mark Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * An abstract <code>EntryBinding</code> that treats a key or data entry as a
+ * tuple; it includes predefined bindings for Java primitive types.
+ *
+ * <p>This class takes care of converting the entries to/from {@link
+ * TupleInput} and {@link TupleOutput} objects. Its two abstract methods must
+ * be implemented by a concrete subclass to convert between tuples and key or
+ * data objects.</p>
+ * <ul>
+ * <li> {@link #entryToObject(TupleInput)} </li>
+ * <li> {@link #objectToEntry(Object,TupleOutput)} </li>
+ * </ul>
+ *
+ * <p>For key or data entries which are Java primitive classes (String,
+ * Integer, etc) {@link #getPrimitiveBinding} may be used to return a builtin
+ * tuple binding. A custom tuple binding for these types is not needed.</p>
+ *
+ * @author Mark Hayes
+ */
+public abstract class TupleBinding implements EntryBinding {
+
+ private static final Map primitives = new HashMap();
+ static {
+ primitives.put(String.class, new StringBinding());
+ primitives.put(Character.class, new CharacterBinding());
+ primitives.put(Boolean.class, new BooleanBinding());
+ primitives.put(Byte.class, new ByteBinding());
+ primitives.put(Short.class, new ShortBinding());
+ primitives.put(Integer.class, new IntegerBinding());
+ primitives.put(Long.class, new LongBinding());
+ primitives.put(Float.class, new FloatBinding());
+ primitives.put(Double.class, new DoubleBinding());
+ }
+
+ /**
+ * Creates a tuple binding.
+ */
+ public TupleBinding() {
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(DatabaseEntry entry) {
+
+ return entryToObject(entryToInput(entry));
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ TupleOutput output = newOutput();
+ objectToEntry(object, output);
+ outputToEntry(output, entry);
+ }
+
+ /**
+ * Utility method for use by bindings to create a tuple output object.
+ *
+ * @return a new tuple output object.
+ */
+ public static TupleOutput newOutput() {
+
+ return new TupleOutput();
+ }
+
+ /**
+ * Utility method for use by bindings to create a tuple output object
+ * with a specific starting size.
+ *
+ * @return a new tuple output object.
+ */
+ public static TupleOutput newOutput(byte[] buffer) {
+
+ return new TupleOutput(buffer);
+ }
+
+ /**
+ * Utility method to set the data in a entry buffer to the data in a tuple
+ * output object.
+ *
+ * @param output is the source tuple output object.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void outputToEntry(TupleOutput output, DatabaseEntry entry) {
+
+ entry.setData(output.getBufferBytes(), output.getBufferOffset(),
+ output.getBufferLength());
+ }
+
+ /**
+ * Utility method to set the data in a entry buffer to the data in a tuple
+ * input object.
+ *
+ * @param input is the source tuple input object.
+ *
+ * @param entry is the destination entry buffer.
+ */
+ public static void inputToEntry(TupleInput input, DatabaseEntry entry) {
+
+ entry.setData(input.getBufferBytes(), input.getBufferOffset(),
+ input.getBufferLength());
+ }
+
+ /**
+ * Utility method to create a new tuple input object for reading the data
+ * from a given buffer. If an existing input is reused, it is reset before
+ * returning it.
+ *
+ * @param entry is the source entry buffer.
+ *
+ * @return the new tuple input object.
+ */
+ public static TupleInput entryToInput(DatabaseEntry entry) {
+
+ return new TupleInput(entry.getData(), entry.getOffset(),
+ entry.getSize());
+ }
+
+ /**
+ * Constructs a key or data object from a {@link TupleInput} entry.
+ *
+ * @param input is the tuple key or data entry.
+ *
+ * @return the key or data object constructed from the entry.
+ */
+ public abstract Object entryToObject(TupleInput input);
+
+ /**
+ * Converts a key or data object to a tuple entry.
+ *
+ * @param object is the key or data object.
+ *
+ * @param output is the tuple entry to which the key or data should be
+ * written.
+ */
+ public abstract void objectToEntry(Object object, TupleOutput output);
+
+ /**
+ * Creates a tuple binding for a primitive Java class. The following
+ * Java classes are supported.
+ * <ul>
+ * <li><code>String</code></li>
+ * <li><code>Character</code></li>
+ * <li><code>Boolean</code></li>
+ * <li><code>Byte</code></li>
+ * <li><code>Short</code></li>
+ * <li><code>Integer</code></li>
+ * <li><code>Long</code></li>
+ * <li><code>Float</code></li>
+ * <li><code>Double</code></li>
+ * </ul>
+ *
+ * @param cls is the primitive Java class.
+ *
+ * @return a new binding for the primitive class or null if the cls
+ * parameter is not one of the supported classes.
+ */
+ public static TupleBinding getPrimitiveBinding(Class cls) {
+
+ return (TupleBinding) primitives.get(cls);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleInput.java b/db/java/src/com/sleepycat/bind/tuple/TupleInput.java
new file mode 100644
index 000000000..97108b10a
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleInput.java
@@ -0,0 +1,482 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleInput.java,v 1.4 2004/09/01 14:34:20 mark Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.util.FastInputStream;
+import com.sleepycat.util.UtfOps;
+
+/**
+ * An <code>InputStream</code> with <code>DataInput</code>-like methods for
+ * reading tuple fields. It is used by <code>TupleBinding</code>.
+ *
+ * <p>This class has many methods that have the same signatures as methods in
+ * the {@link java.io.DataInput} interface. The reason this class does not
+ * implement {@link java.io.DataInput} is because it would break the interface
+ * contract for those methods because of data format differences.</p>
+ *
+ * <p>Signed numbers are stored in the buffer in MSB (most significant byte
+ * first) order with their sign bit (high-order bit) inverted to cause negative
+ * numbers to be sorted first when comparing values as unsigned byte arrays,
+ * as done in a database. Unsigned numbers, including characters, are stored
+ * in MSB order with no change to their sign bit.</p>
+ *
+ * <p>Strings and character arrays are stored either as a fixed length array of
+ * unicode characters, where the length must be known by the application, or as
+ * a null-terminated UTF byte array.</p>
+ * <ul>
+ * <li>Null strings are UTF encoded as { 0xFF }, which is not allowed in a
+ * standard UTF encoding. This allows null strings, as distinct from empty or
+ * zero length strings, to be represented in a tuple. Using the default
+ * comparator, null strings will be ordered last.</li>
+ * <li>Zero (0x0000) character values are UTF encoded as non-zero values, and
+ * therefore embedded zeros in the string are supported. The sequence { 0xC0,
+ * 0x80 } is used to encode a zero character. This UTF encoding is the same
+ * one used by native Java UTF libraries. However, this encoding of zero does
+ * impact the lexicographical ordering, and zeros will not be sorted first (the
+ * natural order) or last. For all character values other than zero, the
+ * default UTF byte ordering is the same as the Unicode lexicographical
+ * character ordering.</li>
+ * </ul>
+ *
+ * <p>Floats and doubles are stored in standard Java integer-bit representation
+ * (IEEE 754). Non-negative numbers are correctly ordered by numeric value.
+ * However, negative numbers are not correctly ordered; therefore, if you use
+ * negative floating point numbers in a key, you'll need to implement and
+ * configure a custom comparator to get correct numeric ordering.</p>
+ *
+ * @author Mark Hayes
+ */
+public class TupleInput extends FastInputStream {
+
+ /**
+ * Creates a tuple input object for reading a byte array of tuple data. A
+ * reference to the byte array will be kept by this object (it will not be
+ * copied) and therefore the byte array should not be modified while this
+ * object is in use.
+ *
+ * @param buffer is the byte array to be read and should contain data in
+ * tuple format.
+ */
+ public TupleInput(byte[] buffer) {
+
+ super(buffer);
+ }
+
+ /**
+ * Creates a tuple input object for reading a byte array of tuple data at
+ * a given offset for a given length. A reference to the byte array will
+ * be kept by this object (it will not be copied) and therefore the byte
+ * array should not be modified while this object is in use.
+ *
+ * @param buffer is the byte array to be read and should contain data in
+ * tuple format.
+ *
+ * @param offset is the byte offset at which to begin reading.
+ *
+ * @param length is the number of bytes to be read.
+ */
+ public TupleInput(byte[] buffer, int offset, int length) {
+
+ super(buffer, offset, length);
+ }
+
+ /**
+ * Creates a tuple input object from the data contained in a tuple output
+ * object. A reference to the tuple output's byte array will be kept by
+ * this object (it will not be copied) and therefore the tuple output
+ * object should not be modified while this object is in use.
+ *
+ * @param output is the tuple output object containing the data to be read.
+ */
+ public TupleInput(TupleOutput output) {
+
+ super(output.getBufferBytes(), output.getBufferOffset(),
+ output.getBufferLength());
+ }
+
+ // --- begin DataInput compatible methods ---
+
+ /**
+ * Reads a null-terminated UTF string from the data buffer and converts
+ * the data from UTF to Unicode.
+ * Reads values that were written using {@link
+ * TupleOutput#writeString(String)}.
+ *
+ * @return the converted string.
+ *
+ * @throws IndexOutOfBoundsException if no null terminating byte is found
+ * in the buffer.
+ *
+ * @throws IllegalArgumentException malformed UTF data is encountered.
+ */
+ public final String readString()
+ throws IndexOutOfBoundsException, IllegalArgumentException {
+
+ byte[] buf = getBufferBytes();
+ int off = getBufferOffset();
+ if (available() >= 2 &&
+ buf[off] == TupleOutput.NULL_STRING_UTF_VALUE &&
+ buf[off + 1] == 0) {
+ skip(2);
+ return null;
+ } else {
+ int byteLen = UtfOps.getZeroTerminatedByteLength(buf, off);
+ skip(byteLen + 1);
+ return UtfOps.bytesToString(buf, off, byteLen);
+ }
+ }
+
+ /**
+ * Reads a char (two byte) unsigned value from the buffer.
+ * Reads values that were written using {@link TupleOutput#writeChar}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final char readChar() throws IndexOutOfBoundsException {
+
+ return (char) readUnsignedShort();
+ }
+
+ /**
+ * Reads a boolean (one byte) unsigned value from the buffer and returns
+ * true if it is non-zero and false if it is zero.
+ * Reads values that were written using {@link TupleOutput#writeBoolean}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final boolean readBoolean() throws IndexOutOfBoundsException {
+
+ int c = readFast();
+ if (c < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ return (c != 0);
+ }
+
+ /**
+ * Reads a signed byte (one byte) value from the buffer.
+ * Reads values that were written using {@link TupleOutput#writeByte}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final byte readByte() throws IndexOutOfBoundsException {
+
+ return (byte) (readUnsignedByte() ^ 0x80);
+ }
+
+ /**
+ * Reads a signed short (two byte) value from the buffer.
+ * Reads values that were written using {@link TupleOutput#writeShort}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final short readShort() throws IndexOutOfBoundsException {
+
+ return (short) (readUnsignedShort() ^ 0x8000);
+ }
+
+ /**
+ * Reads a signed int (four byte) value from the buffer.
+ * Reads values that were written using {@link TupleOutput#writeInt}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final int readInt() throws IndexOutOfBoundsException {
+
+ return (int) (readUnsignedInt() ^ 0x80000000);
+ }
+
+ /**
+ * Reads a signed long (eight byte) value from the buffer.
+ * Reads values that were written using {@link TupleOutput#writeLong}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final long readLong() throws IndexOutOfBoundsException {
+
+ return readUnsignedLong() ^ 0x8000000000000000L;
+ }
+
+ /**
+ * Reads a signed float (four byte) value from the buffer.
+ * Reads values that were written using {@link TupleOutput#writeFloat}.
+ * <code>Float.intBitsToFloat</code> is used to convert the signed int
+ * value.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final float readFloat() throws IndexOutOfBoundsException {
+
+ return Float.intBitsToFloat((int) readUnsignedInt());
+ }
+
+ /**
+ * Reads a signed double (eight byte) value from the buffer.
+ * Reads values that were written using {@link TupleOutput#writeDouble}.
+ * <code>Double.longBitsToDouble</code> is used to convert the signed long
+ * value.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final double readDouble() throws IndexOutOfBoundsException {
+
+ return Double.longBitsToDouble(readUnsignedLong());
+ }
+
+ /**
+ * Reads an unsigned byte (one byte) value from the buffer.
+ * Reads values that were written using {@link
+ * TupleOutput#writeUnsignedByte}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final int readUnsignedByte() throws IndexOutOfBoundsException {
+
+ int c = readFast();
+ if (c < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ return c;
+ }
+
+ /**
+ * Reads an unsigned short (two byte) value from the buffer.
+ * Reads values that were written using {@link
+ * TupleOutput#writeUnsignedShort}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final int readUnsignedShort() throws IndexOutOfBoundsException {
+
+ int c1 = readFast();
+ int c2 = readFast();
+ if ((c1 | c2) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ return ((c1 << 8) | c2);
+ }
+
+ // --- end DataInput compatible methods ---
+
+ /**
+ * Reads an unsigned int (four byte) value from the buffer.
+ * Reads values that were written using {@link
+ * TupleOutput#writeUnsignedInt}.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final long readUnsignedInt() throws IndexOutOfBoundsException {
+
+ long c1 = readFast();
+ long c2 = readFast();
+ long c3 = readFast();
+ long c4 = readFast();
+ if ((c1 | c2 | c3 | c4) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ return ((c1 << 24) | (c2 << 16) | (c3 << 8) | c4);
+ }
+
+ /**
+ * This method is private since an unsigned long cannot be treated as
+ * such in Java, nor converted to a BigInteger of the same value.
+ */
+ private final long readUnsignedLong() throws IndexOutOfBoundsException {
+
+ long c1 = readFast();
+ long c2 = readFast();
+ long c3 = readFast();
+ long c4 = readFast();
+ long c5 = readFast();
+ long c6 = readFast();
+ long c7 = readFast();
+ long c8 = readFast();
+ if ((c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ return ((c1 << 56) | (c2 << 48) | (c3 << 40) | (c4 << 32) |
+ (c5 << 24) | (c6 << 16) | (c7 << 8) | c8);
+ }
+
+ /**
+ * Reads the specified number of bytes from the buffer, converting each
+ * unsigned byte value to a character of the resulting string.
+ * Reads values that were written using {@link TupleOutput#writeBytes}.
+ * Only characters with values below 0x100 may be read using this method.
+ *
+ * @param length is the number of bytes to be read.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final String readBytes(int length)
+ throws IndexOutOfBoundsException {
+
+ StringBuffer buf = new StringBuffer(length);
+ for (int i = 0; i < length; i++) {
+ int c = readFast();
+ if (c < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ buf.append((char) c);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Reads the specified number of characters from the buffer, converting
+ * each two byte unsigned value to a character of the resulting string.
+ * Reads values that were written using {@link TupleOutput#writeChars}.
+ *
+ * @param length is the number of characters to be read.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final String readChars(int length)
+ throws IndexOutOfBoundsException {
+
+ StringBuffer buf = new StringBuffer(length);
+ for (int i = 0; i < length; i++) {
+ buf.append(readChar());
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Reads the specified number of bytes from the buffer, converting each
+ * unsigned byte value to a character of the resulting array.
+ * Reads values that were written using {@link TupleOutput#writeBytes}.
+ * Only characters with values below 0x100 may be read using this method.
+ *
+ * @param chars is the array to receive the data and whose length is used
+ * to determine the number of bytes to be read.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final void readBytes(char[] chars)
+ throws IndexOutOfBoundsException {
+
+ for (int i = 0; i < chars.length; i++) {
+ int c = readFast();
+ if (c < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ chars[i] = (char) c;
+ }
+ }
+
+ /**
+ * Reads the specified number of characters from the buffer, converting
+ * each two byte unsigned value to a character of the resulting array.
+ * Reads values that were written using {@link TupleOutput#writeChars}.
+ *
+ * @param chars is the array to receive the data and whose length is used
+ * to determine the number of characters to be read.
+ *
+ * @return the value read from the buffer.
+ *
+ * @throws IndexOutOfBoundsException if not enough bytes are available in
+ * the buffer.
+ */
+ public final void readChars(char[] chars)
+ throws IndexOutOfBoundsException {
+
+ for (int i = 0; i < chars.length; i++) {
+ chars[i] = readChar();
+ }
+ }
+
+ /**
+ * Reads the specified number of UTF characters string from the data
+ * buffer and converts the data from UTF to Unicode.
+ * Reads values that were written using {@link
+ * TupleOutput#writeString(char[])}.
+ *
+ * @param length is the number of characters to be read.
+ *
+ * @return the converted string.
+ *
+ * @throws IndexOutOfBoundsException if no null terminating byte is found
+ * in the buffer.
+ *
+ * @throws IllegalArgumentException malformed UTF data is encountered.
+ */
+ public final String readString(int length)
+ throws IndexOutOfBoundsException, IllegalArgumentException {
+
+ char[] chars = new char[length];
+ readString(chars);
+ return new String(chars);
+ }
+
+ /**
+ * Reads the specified number of UTF characters string from the data
+ * buffer and converts the data from UTF to Unicode.
+ * Reads values that were written using {@link
+ * TupleOutput#writeString(char[])}.
+ *
+ * @param chars is the array to receive the data and whose length is used
+ * to determine the number of characters to be read.
+ *
+ * @return the converted string.
+ *
+ * @throws IndexOutOfBoundsException if no null terminating byte is found
+ * in the buffer.
+ *
+ * @throws IllegalArgumentException malformed UTF data is encountered.
+ */
+ public final void readString(char[] chars)
+ throws IndexOutOfBoundsException, IllegalArgumentException {
+
+ byte[] buf = getBufferBytes();
+ off = UtfOps.bytesToChars(buf, off, chars, 0, chars.length, false);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleInputBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleInputBinding.java
new file mode 100644
index 000000000..20523dfce
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleInputBinding.java
@@ -0,0 +1,46 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleInputBinding.java,v 1.2 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * A concrete <code>EntryBinding</code> that uses the <code>TupleInput</code>
+ * object as the key or data object.
+ *
+ * A concrete tuple binding for key or data entries which are {@link
+ * TupleInput} objects. This binding is used when tuples themselves are the
+ * objects, rather than using application defined objects. A {@link TupleInput}
+ * must always be used. To convert a {@link TupleOutput} to a {@link
+ * TupleInput}, use the {@link TupleInput#TupleInput(TupleOutput)} constructor.
+ *
+ * @author Mark Hayes
+ */
+public class TupleInputBinding implements EntryBinding {
+
+ /**
+ * Creates a tuple input binding.
+ */
+ public TupleInputBinding() {
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(DatabaseEntry entry) {
+
+ return TupleBinding.entryToInput(entry);
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, DatabaseEntry entry) {
+
+ TupleBinding.inputToEntry((TupleInput) object, entry);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleMarshalledBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleMarshalledBinding.java
new file mode 100644
index 000000000..05644eab0
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleMarshalledBinding.java
@@ -0,0 +1,70 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleMarshalledBinding.java,v 1.3 2004/09/22 18:01:01 bostic Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * A concrete <code>TupleBinding</code> that delegates to the
+ * <code>MarshalledTupleEntry</code> interface of the data or key object.
+ *
+ * <p>This class works by calling the methods of the {@link
+ * MarshalledTupleEntry} interface, which must be implemented by the key or
+ * data class, to convert between the key or data entry and the object.</p>
+ *
+ * @author Mark Hayes
+ */
+public class TupleMarshalledBinding extends TupleBinding {
+
+ private Class cls;
+
+ /**
+ * Creates a tuple marshalled binding object.
+ *
+ * <p>The given class is used to instantiate key or data objects using
+ * {@link Class#forName}, and therefore must be a public class and have a
+ * public no-arguments constructor. It must also implement the {@link
+ * MarshalledTupleEntry} interface.</p>
+ *
+ * @param cls is the class of the key or data objects.
+ */
+ public TupleMarshalledBinding(Class cls) {
+
+ this.cls = cls;
+
+ /* The class will be used to instantiate the object. */
+ if (!MarshalledTupleEntry.class.isAssignableFrom(cls)) {
+ throw new IllegalArgumentException(cls.toString() +
+ " does not implement MarshalledTupleEntry");
+ }
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput input) {
+
+ try {
+ MarshalledTupleEntry obj =
+ (MarshalledTupleEntry) cls.newInstance();
+ obj.unmarshalEntry(input);
+ return obj;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeExceptionWrapper(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+
+ // javadoc is inherited
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ MarshalledTupleEntry obj = (MarshalledTupleEntry) object;
+ obj.marshalEntry(output);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleOutput.java b/db/java/src/com/sleepycat/bind/tuple/TupleOutput.java
new file mode 100644
index 000000000..da1fa0c8f
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleOutput.java
@@ -0,0 +1,398 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleOutput.java,v 1.4 2004/09/01 14:34:20 mark Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.util.FastOutputStream;
+import com.sleepycat.util.UtfOps;
+
+/**
+ * An <code>OutputStream</code> with <code>DataOutput</code>-like methods for
+ * writing tuple fields. It is used by <code>TupleBinding</code>.
+ *
+ * <p>This class has many methods that have the same signatures as methods in
+ * the {@link java.io.DataOutput} interface. The reason this class does not
+ * implement {@link java.io.DataOutput} is because it would break the interface
+ * contract for those methods because of data format differences.</p>
+ *
+ * <p>Signed numbers are stored in the buffer in MSB (most significant byte
+ * first) order with their sign bit (high-order bit) inverted to cause negative
+ * numbers to be sorted first when comparing values as unsigned byte arrays,
+ * as done in a database. Unsigned numbers, including characters, are stored
+ * in MSB order with no change to their sign bit.</p>
+ *
+ * <p>Strings and character arrays are stored either as a fixed length array of
+ * unicode characters, where the length must be known by the application, or as
+ * a null-terminated UTF byte array.</p>
+ * <ul>
+ * <li>Null strings are UTF encoded as { 0xFF }, which is not allowed in a
+ * standard UTF encoding. This allows null strings, as distinct from empty or
+ * zero length strings, to be represented in a tuple. Using the default
+ * comparator, null strings will be ordered last.</li>
+ * <li>Zero (0x0000) character values are UTF encoded as non-zero values, and
+ * therefore embedded zeros in the string are supported. The sequence { 0xC0,
+ * 0x80 } is used to encode a zero character. This UTF encoding is the same
+ * one used by native Java UTF libraries. However, this encoding of zero does
+ * impact the lexicographical ordering, and zeros will not be sorted first (the
+ * natural order) or last. For all character values other than zero, the
+ * default UTF byte ordering is the same as the Unicode lexicographical
+ * character ordering.</li>
+ * </ul>
+ *
+ * <p>Floats and doubles are stored in standard Java integer-bit representation
+ * (IEEE 754). Non-negative numbers are correctly ordered by numeric value.
+ * However, negative numbers are not correctly ordered; therefore, if you use
+ * negative floating point numbers in a key, you'll need to implement and
+ * configure a custom comparator to get correct numeric ordering.</p>
+ *
+ * @author Mark Hayes
+ */
+public class TupleOutput extends FastOutputStream {
+
+ /**
+ * We represent a null string as a single FF UTF character, which cannot
+ * occur in a UTF encoded string.
+ */
+ static final int NULL_STRING_UTF_VALUE = ((byte) 0xFF);
+
+ /**
+ * Creates a tuple output object for writing a byte array of tuple data.
+ */
+ public TupleOutput() {
+
+ super();
+ }
+
+ /**
+ * Creates a tuple output object for writing a byte array of tuple data,
+ * using a given buffer. A new buffer will be allocated only if the number
+ * of bytes needed is greater than the length of this buffer. A reference
+ * to the byte array will be kept by this object and therefore the byte
+ * array should not be modified while this object is in use.
+ *
+ * @param buffer is the byte array to use as the buffer.
+ */
+ public TupleOutput(byte[] buffer) {
+
+ super(buffer);
+ }
+
+ // --- begin DataOutput compatible methods ---
+
+ /**
+ * Writes the specified bytes to the buffer, converting each character to
+ * an unsigned byte value.
+ * Writes values that can be read using {@link TupleInput#readBytes}.
+ * Only characters with values below 0x100 may be written using this
+ * method, since the high-order 8 bits of all characters are discarded.
+ *
+ * @param val is the string containing the values to be written.
+ *
+ * @return this tuple output object.
+ *
+ * @throws NullPointerException if the val parameter is null.
+ */
+ public final TupleOutput writeBytes(String val) {
+
+ writeBytes(val.toCharArray());
+ return this;
+ }
+
+ /**
+ * Writes the specified characters to the buffer, converting each character
+ * to a two byte unsigned value.
+ * Writes values that can be read using {@link TupleInput#readChars}.
+ *
+ * @param val is the string containing the characters to be written.
+ *
+ * @return this tuple output object.
+ *
+ * @throws NullPointerException if the val parameter is null.
+ */
+ public final TupleOutput writeChars(String val) {
+
+ writeChars(val.toCharArray());
+ return this;
+ }
+
+ /**
+ * Writes the specified characters to the buffer, converting each character
+ * to UTF format, and adding a null terminator byte.
+ * Note that zero (0x0000) character values are encoded as non-zero values
+ * and a null String parameter is encoded as 0xFF.
+ * Writes values that can be read using {@link TupleInput#readString()}.
+ *
+ * @param val is the string containing the characters to be written.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeString(String val) {
+
+ if (val != null) {
+ writeString(val.toCharArray());
+ } else {
+ writeFast(NULL_STRING_UTF_VALUE);
+ }
+ writeFast(0);
+ return this;
+ }
+
+ /**
+ * Writes a char (two byte) unsigned value to the buffer.
+ * Writes values that can be read using {@link TupleInput#readChar}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeChar(int val) {
+
+ writeFast((byte) (val >>> 8));
+ writeFast((byte) val);
+ return this;
+ }
+
+ /**
+ * Writes a boolean (one byte) unsigned value to the buffer, writing one
+ * if the value is true and zero if it is false.
+ * Writes values that can be read using {@link TupleInput#readBoolean}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeBoolean(boolean val) {
+
+ writeFast(val ? (byte)1 : (byte)0);
+ return this;
+ }
+
+ /**
+ * Writes an signed byte (one byte) value to the buffer.
+ * Writes values that can be read using {@link TupleInput#readByte}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeByte(int val) {
+
+ writeUnsignedByte(val ^ 0x80);
+ return this;
+ }
+
+ /**
+ * Writes an signed short (two byte) value to the buffer.
+ * Writes values that can be read using {@link TupleInput#readShort}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeShort(int val) {
+
+ writeUnsignedShort(val ^ 0x8000);
+ return this;
+ }
+
+ /**
+ * Writes an signed int (four byte) value to the buffer.
+ * Writes values that can be read using {@link TupleInput#readInt}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeInt(int val) {
+
+ writeUnsignedInt(val ^ 0x80000000);
+ return this;
+ }
+
+ /**
+ * Writes an signed long (eight byte) value to the buffer.
+ * Writes values that can be read using {@link TupleInput#readLong}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeLong(long val) {
+
+ writeUnsignedLong(val ^ 0x8000000000000000L);
+ return this;
+ }
+
+ /**
+ * Writes an signed float (four byte) value to the buffer.
+ * Writes values that can be read using {@link TupleInput#readFloat}.
+ * <code>Float.floatToIntBits</code> is used to convert the signed float
+ * value.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeFloat(float val) {
+
+ writeUnsignedInt(Float.floatToIntBits(val));
+ return this;
+ }
+
+ /**
+ * Writes an signed double (eight byte) value to the buffer.
+ * Writes values that can be read using {@link TupleInput#readDouble}.
+ * <code>Double.doubleToLongBits</code> is used to convert the signed
+ * double value.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeDouble(double val) {
+
+ writeUnsignedLong(Double.doubleToLongBits(val));
+ return this;
+ }
+
+ // --- end DataOutput compatible methods ---
+
+ /**
+ * Writes the specified bytes to the buffer, converting each character to
+ * an unsigned byte value.
+ * Writes values that can be read using {@link TupleInput#readBytes}.
+ * Only characters with values below 0x100 may be written using this
+ * method, since the high-order 8 bits of all characters are discarded.
+ *
+ * @param chars is the array of values to be written.
+ *
+ * @return this tuple output object.
+ *
+ * @throws NullPointerException if the chars parameter is null.
+ */
+ public final TupleOutput writeBytes(char[] chars) {
+
+ for (int i = 0; i < chars.length; i++) {
+ writeFast((byte) chars[i]);
+ }
+ return this;
+ }
+
+ /**
+ * Writes the specified characters to the buffer, converting each character
+ * to a two byte unsigned value.
+ * Writes values that can be read using {@link TupleInput#readChars}.
+ *
+ * @param chars is the array of characters to be written.
+ *
+ * @return this tuple output object.
+ *
+ * @throws NullPointerException if the chars parameter is null.
+ */
+ public final TupleOutput writeChars(char[] chars) {
+
+ for (int i = 0; i < chars.length; i++) {
+ writeFast((byte) (chars[i] >>> 8));
+ writeFast((byte) chars[i]);
+ }
+ return this;
+ }
+
+ /**
+ * Writes the specified characters to the buffer, converting each character
+ * to UTF format.
+ * Note that zero (0x0000) character values are encoded as non-zero values.
+ * Writes values that can be read using {@link TupleInput#readString(int)}
+ * or {@link TupleInput#readString(char[])}.
+ *
+ * @param chars is the array of characters to be written.
+ *
+ * @return this tuple output object.
+ *
+ * @throws NullPointerException if the chars parameter is null.
+ */
+ public final TupleOutput writeString(char[] chars) {
+
+ if (chars.length == 0) return this;
+
+ int utfLength = UtfOps.getByteLength(chars);
+
+ makeSpace(utfLength);
+ UtfOps.charsToBytes(chars, 0, getBufferBytes(), getBufferLength(),
+ chars.length);
+ addSize(utfLength);
+ return this;
+ }
+
+ /**
+ * Writes an unsigned byte (one byte) value to the buffer.
+ * Writes values that can be read using {@link
+ * TupleInput#readUnsignedByte}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeUnsignedByte(int val) {
+
+ writeFast(val);
+ return this;
+ }
+
+ /**
+ * Writes an unsigned short (two byte) value to the buffer.
+ * Writes values that can be read using {@link
+ * TupleInput#readUnsignedShort}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeUnsignedShort(int val) {
+
+ writeFast((byte) (val >>> 8));
+ writeFast((byte) val);
+ return this;
+ }
+
+ /**
+ * Writes an unsigned int (four byte) value to the buffer.
+ * Writes values that can be read using {@link
+ * TupleInput#readUnsignedInt}.
+ *
+ * @param val is the value to write to the buffer.
+ *
+ * @return this tuple output object.
+ */
+ public final TupleOutput writeUnsignedInt(long val) {
+
+ writeFast((byte) (val >>> 24));
+ writeFast((byte) (val >>> 16));
+ writeFast((byte) (val >>> 8));
+ writeFast((byte) val);
+ return this;
+ }
+
+ /**
+ * This method is private since an unsigned long cannot be treated as
+ * such in Java, nor converted to a BigInteger of the same value.
+ */
+ private final TupleOutput writeUnsignedLong(long val) {
+
+ writeFast((byte) (val >>> 56));
+ writeFast((byte) (val >>> 48));
+ writeFast((byte) (val >>> 40));
+ writeFast((byte) (val >>> 32));
+ writeFast((byte) (val >>> 24));
+ writeFast((byte) (val >>> 16));
+ writeFast((byte) (val >>> 8));
+ writeFast((byte) val);
+ return this;
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleTupleBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleTupleBinding.java
new file mode 100644
index 000000000..ac8f4158f
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleTupleBinding.java
@@ -0,0 +1,96 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleTupleBinding.java,v 1.2 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * An abstract <code>EntityBinding</code> that treats an entity's key entry and
+ * data entry as tuples.
+ *
+ * <p>This class takes care of converting the entries to/from {@link
+ * TupleInput} and {@link TupleOutput} objects. Its three abstract methods
+ * must be implemented by a concrete subclass to convert between tuples and
+ * entity objects.</p>
+ * <ul>
+ * <li> {@link #entryToObject(TupleInput,TupleInput)} </li>
+ * <li> {@link #objectToKey(Object,TupleOutput)} </li>
+ * <li> {@link #objectToData(Object,TupleOutput)} </li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public abstract class TupleTupleBinding implements EntityBinding {
+
+ /**
+ * Creates a tuple-tuple entity binding.
+ */
+ public TupleTupleBinding() {
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(DatabaseEntry key, DatabaseEntry data) {
+
+ return entryToObject(TupleBinding.entryToInput(key),
+ TupleBinding.entryToInput(data));
+ }
+
+ // javadoc is inherited
+ public void objectToKey(Object object, DatabaseEntry key) {
+
+ TupleOutput output = TupleBinding.newOutput();
+ objectToKey(object, output);
+ TupleBinding.outputToEntry(output, key);
+ }
+
+ // javadoc is inherited
+ public void objectToData(Object object, DatabaseEntry data) {
+
+ TupleOutput output = TupleBinding.newOutput();
+ objectToData(object, output);
+ TupleBinding.outputToEntry(output, data);
+ }
+
+ // abstract methods
+
+ /**
+ * Constructs an entity object from {@link TupleInput} key and data
+ * entries.
+ *
+ * @param keyInput is the {@link TupleInput} key entry object.
+ *
+ * @param dataInput is the {@link TupleInput} data entry object.
+ *
+ * @return the entity object constructed from the key and data.
+ */
+ public abstract Object entryToObject(TupleInput keyInput,
+ TupleInput dataInput);
+
+ /**
+ * Extracts a key tuple from an entity object.
+ *
+ * @param object is the entity object.
+ *
+ * @param output is the {@link TupleOutput} to which the key should be
+ * written.
+ */
+ public abstract void objectToKey(Object object, TupleOutput output);
+
+ /**
+ * Extracts a key tuple from an entity object.
+ *
+ * @param object is the entity object.
+ *
+ * @param output is the {@link TupleOutput} to which the data should be
+ * written.
+ */
+ public abstract void objectToData(Object object, TupleOutput output);
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleTupleKeyCreator.java b/db/java/src/com/sleepycat/bind/tuple/TupleTupleKeyCreator.java
new file mode 100644
index 000000000..f5041a94e
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleTupleKeyCreator.java
@@ -0,0 +1,105 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleTupleKeyCreator.java,v 1.4 2004/08/02 18:52:05 mjc Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.db.SecondaryKeyCreator;
+
+/**
+ * An abstract key creator that uses a tuple key and a tuple data entry. This
+ * class takes care of converting the key and data entry to/from {@link
+ * TupleInput} and {@link TupleOutput} objects.
+ *
+ * @author Mark Hayes
+ */
+public abstract class TupleTupleKeyCreator
+ implements SecondaryKeyCreator {
+
+ /**
+ * Creates a tuple-tuple key creator.
+ */
+ public TupleTupleKeyCreator() {
+ }
+
+ // javadoc is inherited
+ public boolean createSecondaryKey(SecondaryDatabase db,
+ DatabaseEntry primaryKeyEntry,
+ DatabaseEntry dataEntry,
+ DatabaseEntry indexKeyEntry)
+ throws DatabaseException {
+
+ TupleOutput output = TupleBinding.newOutput();
+ TupleInput primaryKeyInput =
+ TupleBinding.entryToInput(primaryKeyEntry);
+ TupleInput dataInput = TupleBinding.entryToInput(dataEntry);
+ if (createSecondaryKey(primaryKeyInput, dataInput, output)) {
+ TupleBinding.outputToEntry(output, indexKeyEntry);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // javadoc is inherited
+ public boolean nullifyForeignKey(SecondaryDatabase db,
+ DatabaseEntry dataEntry)
+ throws DatabaseException {
+
+ TupleOutput output = TupleBinding.newOutput();
+ if (nullifyForeignKey(TupleBinding.entryToInput(dataEntry),
+ output)) {
+ TupleBinding.outputToEntry(output, dataEntry);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Creates the index key from primary key tuple and data tuple.
+ *
+ * @param primaryKeyInput is the {@link TupleInput} for the primary key
+ * entry.
+ *
+ * @param dataInput is the {@link TupleInput} for the data entry.
+ *
+ * @param indexKeyOutput is the destination index key tuple.
+ *
+ * @return true if a key was created, or false to indicate that the key is
+ * not present.
+ */
+ public abstract boolean createSecondaryKey(TupleInput primaryKeyInput,
+ TupleInput dataInput,
+ TupleOutput indexKeyOutput);
+
+ /**
+ * Clears the index key in the tuple data entry. The dataInput should be
+ * read and then written to the dataOutput, clearing the index key in the
+ * process.
+ *
+ * <p>The secondary key should be output or removed by this method such
+ * that {@link #createSecondaryKey} will return false. Other fields in the
+ * data object should remain unchanged.</p>
+ *
+ * @param dataInput is the {@link TupleInput} for the data entry.
+ *
+ * @param dataOutput is the destination {@link TupleOutput}.
+ *
+ * @return true if the key was cleared, or false to indicate that the key
+ * is not present and no change is necessary.
+ */
+ public boolean nullifyForeignKey(TupleInput dataInput,
+ TupleOutput dataOutput) {
+
+ return false;
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledBinding.java
new file mode 100644
index 000000000..370b4cc8a
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledBinding.java
@@ -0,0 +1,94 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleTupleMarshalledBinding.java,v 1.3 2004/09/22 18:01:01 bostic Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * A concrete <code>TupleTupleBinding</code> that delegates to the
+ * <code>MarshalledTupleEntry</code> and
+ * <code>MarshalledTupleKeyEntity</code> interfaces of the entity class.
+ *
+ * <p>This class calls the methods of the {@link MarshalledTupleEntry}
+ * interface to convert between the data entry and entity object. It calls the
+ * methods of the {@link MarshalledTupleKeyEntity} interface to convert between
+ * the key entry and the entity object. These two interfaces must both be
+ * implemented by the entity class.</p>
+ *
+ * @author Mark Hayes
+ */
+public class TupleTupleMarshalledBinding extends TupleTupleBinding {
+
+ private Class cls;
+
+ /**
+ * Creates a tuple-tuple marshalled binding object.
+ *
+ * <p>The given class is used to instantiate entity objects using
+ * {@link Class#forName}, and therefore must be a public class and have a
+ * public no-arguments constructor. It must also implement the {@link
+ * MarshalledTupleEntry} and {@link MarshalledTupleKeyEntity}
+ * interfaces.</p>
+ *
+ * @param cls is the class of the entity objects.
+ */
+ public TupleTupleMarshalledBinding(Class cls) {
+
+ this.cls = cls;
+
+ // The entity class will be used to instantiate the entity object.
+ //
+ if (!MarshalledTupleKeyEntity.class.isAssignableFrom(cls)) {
+ throw new IllegalArgumentException(cls.toString() +
+ " does not implement MarshalledTupleKeyEntity");
+ }
+ if (!MarshalledTupleEntry.class.isAssignableFrom(cls)) {
+ throw new IllegalArgumentException(cls.toString() +
+ " does not implement MarshalledTupleEntry");
+ }
+ }
+
+ // javadoc is inherited
+ public Object entryToObject(TupleInput keyInput, TupleInput dataInput) {
+
+ // This "tricky" binding returns the stored data as the entity, but
+ // first it sets the transient key fields from the stored key.
+ MarshalledTupleEntry obj;
+ try {
+ obj = (MarshalledTupleEntry) cls.newInstance();
+ } catch (IllegalAccessException e) {
+ throw new RuntimeExceptionWrapper(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ if (dataInput != null) { // may be null if used by key extractor
+ obj.unmarshalEntry(dataInput);
+ }
+ MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) obj;
+ if (keyInput != null) { // may be null if used by key extractor
+ entity.unmarshalPrimaryKey(keyInput);
+ }
+ return entity;
+ }
+
+ // javadoc is inherited
+ public void objectToKey(Object object, TupleOutput output) {
+
+ MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) object;
+ entity.marshalPrimaryKey(output);
+ }
+
+ // javadoc is inherited
+ public void objectToData(Object object, TupleOutput output) {
+
+ MarshalledTupleEntry entity = (MarshalledTupleEntry) object;
+ entity.marshalEntry(output);
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledKeyCreator.java b/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledKeyCreator.java
new file mode 100644
index 000000000..aa2911a91
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledKeyCreator.java
@@ -0,0 +1,75 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleTupleMarshalledKeyCreator.java,v 1.2 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.bind.tuple;
+
+/**
+ * A concrete key creator that works in conjunction with a {@link
+ * TupleTupleMarshalledBinding}. This key creator works by calling the
+ * methods of the {@link MarshalledTupleKeyEntity} interface to create and
+ * clear the index key.
+ *
+ * <p>Note that a marshalled tuple key creator is somewhat less efficient
+ * than a non-marshalled key tuple creator because more conversions are
+ * needed. A marshalled key creator must convert the entry to an object in
+ * order to create the key, while an unmarshalled key creator does not.</p>
+ *
+ * @author Mark Hayes
+ */
+public class TupleTupleMarshalledKeyCreator extends TupleTupleKeyCreator {
+
+ private String keyName;
+ private TupleTupleMarshalledBinding binding;
+
+ /**
+ * Creates a tuple-tuple marshalled key creator.
+ *
+ * @param binding is the binding used for the tuple-tuple entity.
+ *
+ * @param keyName is the key name passed to the {@link
+ * MarshalledTupleKeyEntity#marshalSecondaryKey} method to identify the
+ * index key.
+ */
+ public TupleTupleMarshalledKeyCreator(TupleTupleMarshalledBinding binding,
+ String keyName) {
+
+ this.binding = binding;
+ this.keyName = keyName;
+ }
+
+ // javadoc is inherited
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ TupleInput dataInput,
+ TupleOutput indexKeyOutput) {
+
+ /* The primary key is unmarshalled before marshalling the index key, to
+ * account for cases where the index key includes fields taken from the
+ * primary key.
+ */
+ MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity)
+ binding.entryToObject(primaryKeyInput, dataInput);
+
+ return entity.marshalSecondaryKey(keyName, indexKeyOutput);
+ }
+
+ // javadoc is inherited
+ public boolean nullifyForeignKey(TupleInput dataInput,
+ TupleOutput dataOutput) {
+
+ // XXX null primary key input below may be unexpected by the binding
+ MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity)
+ binding.entryToObject(null, dataInput);
+ if (entity.nullifyForeignKey(keyName)) {
+ binding.objectToData(entity, dataOutput);
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/bind/tuple/package.html b/db/java/src/com/sleepycat/bind/tuple/package.html
new file mode 100644
index 000000000..9f2723523
--- /dev/null
+++ b/db/java/src/com/sleepycat/bind/tuple/package.html
@@ -0,0 +1,6 @@
+<!-- $Id: package.html,v 1.1 2004/08/02 18:52:05 mjc Exp $ -->
+<html>
+<body>
+Bindings that use sequences of primitive fields, or tuples.
+</body>
+</html>
diff --git a/db/java/src/com/sleepycat/collections/CurrentTransaction.java b/db/java/src/com/sleepycat/collections/CurrentTransaction.java
new file mode 100644
index 000000000..1876f26fc
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/CurrentTransaction.java
@@ -0,0 +1,433 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: CurrentTransaction.java,v 1.4 2004/09/22 18:01:02 bostic Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.WeakHashMap;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.CursorConfig;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.LockMode;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.db.TransactionConfig;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * Provides access to the current transaction for the current thread within the
+ * context of a Berkeley DB environment. This class provides explicit
+ * transaction control beyond that provided by the {@link TransactionRunner}
+ * class. However, both methods of transaction control manage per-thread
+ * transactions.
+ *
+ * @author Mark Hayes
+ */
+public class CurrentTransaction {
+
+ /* For internal use, this class doubles as an Environment wrapper. */
+
+ private static WeakHashMap envMap = new WeakHashMap();
+
+ private LockMode writeLockMode;
+ private boolean cdbMode;
+ private boolean txnMode;
+ private Environment env;
+ private ThreadLocal localTrans = new ThreadLocal();
+ private ThreadLocal localCdbCursors;
+
+ /**
+ * Gets the CurrentTransaction accessor for a specified Berkeley DB
+ * environment. This method always returns the same reference when called
+ * more than once with the same environment parameter.
+ *
+ * @param env is an open Berkeley DB environment.
+ *
+ * @return the CurrentTransaction accessor for the given environment, or
+ * null if the environment is not transactional.
+ */
+ public static CurrentTransaction getInstance(Environment env) {
+
+ CurrentTransaction currentTxn = getInstanceInternal(env);
+ return currentTxn.isTxnMode() ? currentTxn : null;
+ }
+
+ /**
+ * Gets the CurrentTransaction accessor for a specified Berkeley DB
+ * environment. Unlike getInstance(), this method never returns null.
+ *
+ * @param env is an open Berkeley DB environment.
+ */
+ static CurrentTransaction getInstanceInternal(Environment env) {
+ synchronized (envMap) {
+ CurrentTransaction myEnv = (CurrentTransaction) envMap.get(env);
+ if (myEnv == null) {
+ myEnv = new CurrentTransaction(env);
+ envMap.put(env, myEnv);
+ }
+ return myEnv;
+ }
+ }
+
+ private CurrentTransaction(Environment env) {
+ this.env = env;
+ try {
+ EnvironmentConfig config = env.getConfig();
+ txnMode = config.getTransactional();
+ if (txnMode || DbCompat.getInitializeLocking(config)) {
+ writeLockMode = LockMode.RMW;
+ } else {
+ writeLockMode = LockMode.DEFAULT;
+ }
+ cdbMode = DbCompat.getInitializeCDB(config);
+ if (cdbMode) {
+ localCdbCursors = new ThreadLocal();
+ }
+ } catch (DatabaseException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+
+ /**
+ * Returns whether this is a transactional environment.
+ */
+ final boolean isTxnMode() {
+
+ return txnMode;
+ }
+
+ /**
+ * Returns whether this is a Concurrent Data Store environment.
+ */
+ final boolean isCdbMode() {
+
+ return cdbMode;
+ }
+
+ /**
+ * Return the LockMode.RMW or null, depending on whether locking is
+ * enabled. LockMode.RMW will cause an error if passed when locking
+ * is not enabled. Locking is enabled if locking or transactions were
+ * specified for this environment.
+ */
+ final LockMode getWriteLockMode() {
+
+ return writeLockMode;
+ }
+
+ /**
+ * Returns the underlying Berkeley DB environment.
+ */
+ public final Environment getEnvironment() {
+
+ return env;
+ }
+
+ /**
+ * Returns the transaction associated with the current thread for this
+ * environment, or null if no transaction is active.
+ */
+ public final Transaction getTransaction() {
+
+ Trans trans = (Trans) localTrans.get();
+ return (trans != null) ? trans.txn : null;
+ }
+
+ /**
+ * Begins a new transaction for this environment and associates it with
+ * the current thread. If a transaction is already active for this
+ * environment and thread, a nested transaction will be created.
+ *
+ * @param config the transaction configuration used for calling
+ * {@link Environment#beginTransaction}, or null to use the default
+ * configuration.
+ *
+ * @return the new transaction.
+ *
+ * @throws DatabaseException if the transaction cannot be started, in which
+ * case any existing transaction is not affected.
+ *
+ * @throws IllegalStateException if a transaction is already active and
+ * nested transactions are not supported by the environment.
+ */
+ public final Transaction beginTransaction(TransactionConfig config)
+ throws DatabaseException {
+
+ Trans trans = (Trans) localTrans.get();
+ if (trans != null) {
+ if (trans.txn != null) {
+ if (!DbCompat.NESTED_TRANSACTIONS) {
+ throw new IllegalStateException(
+ "Nested transactions are not supported");
+ }
+ Transaction parentTxn = trans.txn;
+ trans = new Trans(trans, config);
+ trans.txn = env.beginTransaction(parentTxn, config);
+ localTrans.set(trans);
+ } else {
+ trans.txn = env.beginTransaction(null, config);
+ trans.config = config;
+ }
+ } else {
+ trans = new Trans(null, config);
+ trans.txn = env.beginTransaction(null, config);
+ localTrans.set(trans);
+ }
+ return trans.txn;
+ }
+
+ /**
+ * Commits the transaction that is active for the current thread for this
+ * environment and makes the parent transaction (if any) the current
+ * transaction.
+ *
+ * @return the parent transaction or null if the committed transaction was
+ * not nested.
+ *
+ * @throws DatabaseException if an error occurs committing the transaction.
+ * The transaction will still be closed and the parent transaction will
+ * become the current transaction.
+ *
+ * @throws IllegalStateException if no transaction is active for the
+ * current thread for this environment.
+ */
+ public final Transaction commitTransaction()
+ throws DatabaseException, IllegalStateException {
+
+ Trans trans = (Trans) localTrans.get();
+ if (trans != null && trans.txn != null) {
+ Transaction parent = closeTxn(trans);
+ trans.txn.commit();
+ return parent;
+ } else {
+ throw new IllegalStateException("No transaction is active");
+ }
+ }
+
+ /**
+ * Aborts the transaction that is active for the current thread for this
+ * environment and makes the parent transaction (if any) the current
+ * transaction.
+ *
+ * @return the parent transaction or null if the aborted transaction was
+ * not nested.
+ *
+ * @throws DatabaseException if an error occurs aborting the transaction.
+ * The transaction will still be closed and the parent transaction will
+ * become the current transaction.
+ *
+ * @throws IllegalStateException if no transaction is active for the
+ * current thread for this environment.
+ */
+ public final Transaction abortTransaction()
+ throws DatabaseException, IllegalStateException {
+
+ Trans trans = (Trans) localTrans.get();
+ if (trans != null && trans.txn != null) {
+ Transaction parent = closeTxn(trans);
+ trans.txn.abort();
+ return parent;
+ } else {
+ throw new IllegalStateException("No transaction is active");
+ }
+ }
+
+ /**
+ * Returns whether the current transaction is a dirtyRead transaction.
+ */
+ final boolean isDirtyRead() {
+
+ Trans trans = (Trans) localTrans.get();
+ if (trans != null && trans.config != null) {
+ return trans.config.getDirtyRead();
+ } else {
+ return false;
+ }
+ }
+
+ private Transaction closeTxn(Trans trans) {
+
+ localTrans.set(trans.parent);
+ return (trans.parent != null) ? trans.parent.txn : null;
+ }
+
+ private static class Trans {
+
+ private Trans parent;
+ private Transaction txn;
+ private TransactionConfig config;
+
+ private Trans(Trans parent, TransactionConfig config) {
+
+ this.parent = parent;
+ this.config = config;
+ }
+ }
+
+ /**
+ * Opens a cursor for a given database, dup'ing an existing CDB cursor if
+ * one is open for the current thread.
+ */
+ Cursor openCursor(Database db, boolean writeCursor, Transaction txn)
+ throws DatabaseException {
+
+ if (cdbMode) {
+ CdbCursors cdbCursors = null;
+ WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
+ if (cdbCursorsMap == null) {
+ cdbCursorsMap = new WeakHashMap();
+ localCdbCursors.set(cdbCursorsMap);
+ } else {
+ cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
+ }
+ if (cdbCursors == null) {
+ cdbCursors = new CdbCursors();
+ cdbCursorsMap.put(db, cdbCursors);
+ }
+ List cursors;
+ CursorConfig config;
+ if (writeCursor) {
+ if (cdbCursors.readCursors.size() > 0) {
+
+ /*
+ * Although CDB allows opening a write cursor when a read
+ * cursor is open, a self-deadlock will occur if a write is
+ * attempted for a record that is read-locked; we should
+ * avoid self-deadlocks at all costs
+ */
+ throw new IllegalStateException(
+ "cannot open CDB write cursor when read cursor is open");
+ }
+ cursors = cdbCursors.writeCursors;
+ config = new CursorConfig();
+ DbCompat.setWriteCursor(config, true);
+ } else {
+ cursors = cdbCursors.readCursors;
+ config = null;
+ }
+ Cursor cursor;
+ if (cursors.size() > 0) {
+ Cursor other = ((Cursor) cursors.get(0));
+ cursor = other.dup(false);
+ } else {
+ cursor = db.openCursor(null, config);
+ }
+ cursors.add(cursor);
+ return cursor;
+ } else {
+ return db.openCursor(txn, null);
+ }
+ }
+
+ /**
+ * Duplicates a cursor for a given database.
+ *
+ * @param writeCursor true to open a write cursor in a CDB environment, and
+ * ignored for other environments.
+ *
+ * @param samePosition is passed through to Cursor.dup().
+ *
+ * @return the open cursor.
+ *
+ * @throws DatabaseException if a database problem occurs.
+ */
+ Cursor dupCursor(Cursor cursor, boolean writeCursor, boolean samePosition)
+ throws DatabaseException {
+
+ if (cdbMode) {
+ WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
+ if (cdbCursorsMap != null) {
+ Database db = cursor.getDatabase();
+ CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
+ if (cdbCursors != null) {
+ List cursors = writeCursor ? cdbCursors.writeCursors
+ : cdbCursors.readCursors;
+ if (cursors.contains(cursor)) {
+ Cursor newCursor = cursor.dup(samePosition);
+ cursors.add(newCursor);
+ return newCursor;
+ }
+ }
+ }
+ throw new IllegalStateException("cursor to dup not tracked");
+ } else {
+ return cursor.dup(samePosition);
+ }
+ }
+
+ /**
+ * Closes a cursor.
+ *
+ * @param cursor the cursor to close.
+ *
+ * @throws DatabaseException if a database problem occurs.
+ */
+ void closeCursor(Cursor cursor)
+ throws DatabaseException {
+
+ if (cursor == null) {
+ return;
+ }
+ if (cdbMode) {
+ WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
+ if (cdbCursorsMap != null) {
+ Database db = cursor.getDatabase();
+ CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
+ if (cdbCursors != null) {
+ if (cdbCursors.readCursors.remove(cursor) ||
+ cdbCursors.writeCursors.remove(cursor)) {
+ cursor.close();
+ return;
+ }
+ }
+ }
+ throw new IllegalStateException(
+ "closing CDB cursor that was not known to be open");
+ } else {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Returns true if a CDB cursor is open and therefore a Database write
+ * operation should not be attempted since a self-deadlock may result.
+ */
+ boolean isCDBCursorOpen(Database db)
+ throws DatabaseException {
+
+ if (cdbMode) {
+ WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
+ if (cdbCursorsMap != null) {
+ CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
+
+ /*
+ * FindBugs whines unnecessarily about a Null pointer
+ * dereference here.
+ */
+ if (cdbCursors != null &&
+ cdbCursors.readCursors.size() > 0 ||
+ cdbCursors.writeCursors.size() > 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static final class CdbCursors {
+
+ List writeCursors = new ArrayList();
+ List readCursors = new ArrayList();
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/DataCursor.java b/db/java/src/com/sleepycat/collections/DataCursor.java
new file mode 100644
index 000000000..cc77e7a27
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/DataCursor.java
@@ -0,0 +1,690 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DataCursor.java,v 1.4 2004/09/22 18:01:02 bostic Exp $
+ */
+
+package com.sleepycat.collections;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.JoinConfig;
+import com.sleepycat.db.JoinCursor;
+import com.sleepycat.db.LockMode;
+import com.sleepycat.db.OperationStatus;
+
+/**
+ * Represents a Berkeley DB cursor and adds support for indices, bindings and
+ * key ranges.
+ *
+ * <p>This class operates on a view and takes care of reading and updating
+ * indices, calling bindings, constraining access to a key range, etc.</p>
+ *
+ * @author Mark Hayes
+ */
+final class DataCursor implements Cloneable {
+
+ private RangeCursor cursor;
+ private JoinCursor joinCursor;
+ private DataView view;
+ private KeyRange range;
+ private boolean writeAllowed;
+ private boolean dirtyRead;
+ private DatabaseEntry keyThang;
+ private DatabaseEntry valueThang;
+ private DatabaseEntry primaryKeyThang;
+ private DatabaseEntry otherThang;
+ private DataCursor[] indexCursorsToClose;
+
+ /**
+ * Creates a cursor for a given view.
+ */
+ DataCursor(DataView view, boolean writeAllowed)
+ throws DatabaseException {
+
+ init(view, writeAllowed, null);
+ }
+
+ /**
+ * Creates a cursor for a given view and single key range.
+ */
+ DataCursor(DataView view, boolean writeAllowed, Object singleKey)
+ throws DatabaseException {
+
+ init(view, writeAllowed, view.subRange(singleKey));
+ }
+
+ /**
+ * Creates a cursor for a given view and key range.
+ */
+ DataCursor(DataView view, boolean writeAllowed,
+ Object beginKey, boolean beginInclusive,
+ Object endKey, boolean endInclusive)
+ throws DatabaseException {
+
+ init(view, writeAllowed,
+ view.subRange(beginKey, beginInclusive, endKey, endInclusive));
+ }
+
+ /**
+ * Creates a join cursor.
+ */
+ DataCursor(DataView view, DataCursor[] indexCursors,
+ JoinConfig joinConfig, boolean closeIndexCursors)
+ throws DatabaseException {
+
+ if (view.isSecondary()) {
+ throw new IllegalArgumentException(
+ "The primary collection in a join must not be a secondary " +
+ "database");
+ }
+ Cursor[] cursors = new Cursor[indexCursors.length];
+ for (int i = 0; i < cursors.length; i += 1) {
+ cursors[i] = indexCursors[i].cursor.getCursor();
+ }
+ joinCursor = view.db.join(cursors, joinConfig);
+ init(view, false, null);
+ if (closeIndexCursors) {
+ indexCursorsToClose = indexCursors;
+ }
+ }
+
+ /**
+ * Clones a cursor preserving the current position.
+ */
+ DataCursor cloneCursor()
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+
+ DataCursor o;
+ try {
+ o = (DataCursor) super.clone();
+ } catch (CloneNotSupportedException neverHappens) {
+ return null;
+ }
+
+ o.initThangs();
+ KeyRange.copy(keyThang, o.keyThang);
+ KeyRange.copy(valueThang, o.valueThang);
+ if (primaryKeyThang != keyThang) {
+ KeyRange.copy(primaryKeyThang, o.primaryKeyThang);
+ }
+
+ o.cursor = cursor.dup(true);
+ return o;
+ }
+
+ /**
+ * Returns the internal range cursor.
+ */
+ RangeCursor getCursor() {
+ return cursor;
+ }
+
+ /**
+ * Constructor helper.
+ */
+ private void init(DataView view, boolean writeAllowed, KeyRange range)
+ throws DatabaseException {
+
+ this.view = view;
+ this.writeAllowed = writeAllowed && view.writeAllowed;
+ this.range = (range != null) ? range : view.range;
+ dirtyRead = view.dirtyReadEnabled;
+
+ initThangs();
+
+ if (joinCursor == null) {
+ cursor = new RangeCursor(view, this.range, this.writeAllowed);
+ }
+ }
+
+ /**
+ * Constructor helper.
+ */
+ private void initThangs()
+ throws DatabaseException {
+
+ keyThang = new DatabaseEntry();
+ primaryKeyThang = view.isSecondary() ? (new DatabaseEntry())
+ : keyThang;
+ valueThang = new DatabaseEntry();
+ }
+
+ /**
+ * Closes the associated cursor.
+ */
+ void close()
+ throws DatabaseException {
+
+ if (joinCursor != null) {
+ JoinCursor toClose = joinCursor;
+ joinCursor = null;
+ toClose.close();
+ }
+ if (cursor != null) {
+ Cursor toClose = cursor.getCursor();
+ cursor = null;
+ view.currentTxn.closeCursor(toClose );
+ }
+ if (indexCursorsToClose != null) {
+ DataCursor[] toClose = indexCursorsToClose;
+ indexCursorsToClose = null;
+ for (int i = 0; i < toClose.length; i += 1) {
+ toClose[i].close();
+ }
+ }
+ }
+
+ /**
+ * Returns the view for this cursor.
+ */
+ DataView getView() {
+
+ return view;
+ }
+
+ /**
+ * Returns the range for this cursor.
+ */
+ KeyRange getRange() {
+
+ return range;
+ }
+
+ /**
+ * Returns whether write is allowed for this cursor, as specified to the
+ * constructor.
+ */
+ boolean isWriteAllowed() {
+
+ return writeAllowed;
+ }
+
+ /**
+ * Returns the key object for the last record read.
+ */
+ Object getCurrentKey()
+ throws DatabaseException {
+
+ if (view.keyBinding == null) {
+ throw new UnsupportedOperationException();
+ }
+ return view.makeKey(keyThang);
+ }
+
+ /**
+ * Returns the value object for the last record read.
+ */
+ Object getCurrentValue()
+ throws DatabaseException {
+
+ return view.makeValue(primaryKeyThang, valueThang);
+ }
+
+ /**
+ * Returns whether record number access is allowed.
+ */
+ boolean hasRecNumAccess() {
+
+ return view.recNumAccess;
+ }
+
+ /**
+ * Returns the record number for the last record read.
+ */
+ int getCurrentRecordNumber()
+ throws DatabaseException {
+
+ if (view.btreeRecNumDb) {
+ /* BTREE-RECNO access. */
+ if (otherThang == null) {
+ otherThang = new DatabaseEntry();
+ }
+ DbCompat.getCurrentRecordNumber(cursor.getCursor(), otherThang,
+ getLockMode(false));
+ return DbCompat.getRecordNumber(otherThang);
+ } else {
+ /* QUEUE or RECNO database. */
+ return DbCompat.getRecordNumber(keyThang);
+ }
+ }
+
+ /**
+ * Binding version of Cursor.getCurrent(), no join cursor allowed.
+ */
+ OperationStatus getCurrent(boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ return cursor.getCurrent(keyThang, primaryKeyThang, valueThang,
+ getLockMode(lockForWrite));
+ }
+
+ /**
+ * Binding version of Cursor.getFirst(), join cursor is allowed.
+ */
+ OperationStatus getFirst(boolean lockForWrite)
+ throws DatabaseException {
+
+ LockMode lockMode = getLockMode(lockForWrite);
+ if (joinCursor != null) {
+ return joinCursor.getNext(keyThang, valueThang, lockMode);
+ } else {
+ return cursor.getFirst(keyThang, primaryKeyThang, valueThang,
+ lockMode);
+ }
+ }
+
+ /**
+ * Binding version of Cursor.getNext(), join cursor is allowed.
+ */
+ OperationStatus getNext(boolean lockForWrite)
+ throws DatabaseException {
+
+ LockMode lockMode = getLockMode(lockForWrite);
+ if (joinCursor != null) {
+ return joinCursor.getNext(keyThang, valueThang, lockMode);
+ } else {
+ return cursor.getNext(keyThang, primaryKeyThang, valueThang,
+ lockMode);
+ }
+ }
+
+ /**
+ * Binding version of Cursor.getNext(), join cursor is allowed.
+ */
+ OperationStatus getNextNoDup(boolean lockForWrite)
+ throws DatabaseException {
+
+ LockMode lockMode = getLockMode(lockForWrite);
+ if (joinCursor != null) {
+ return joinCursor.getNext(keyThang, valueThang, lockMode);
+ } else {
+ return cursor.getNextNoDup(keyThang, primaryKeyThang, valueThang,
+ lockMode);
+ }
+ }
+
+ /**
+ * Binding version of Cursor.getNextDup(), no join cursor allowed.
+ */
+ OperationStatus getNextDup(boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ return cursor.getNextDup(keyThang, primaryKeyThang, valueThang,
+ getLockMode(lockForWrite));
+ }
+
+ /**
+ * Binding version of Cursor.getLast(), no join cursor allowed.
+ */
+ OperationStatus getLast(boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ return cursor.getLast(keyThang, primaryKeyThang, valueThang,
+ getLockMode(lockForWrite));
+ }
+
+ /**
+ * Binding version of Cursor.getPrev(), no join cursor allowed.
+ */
+ OperationStatus getPrev(boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ return cursor.getPrev(keyThang, primaryKeyThang, valueThang,
+ getLockMode(lockForWrite));
+ }
+
+ /**
+ * Binding version of Cursor.getPrevNoDup(), no join cursor allowed.
+ */
+ OperationStatus getPrevNoDup(boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ return cursor.getPrevNoDup(keyThang, primaryKeyThang, valueThang,
+ getLockMode(lockForWrite));
+ }
+
+ /**
+ * Binding version of Cursor.getPrevDup(), no join cursor allowed.
+ */
+ OperationStatus getPrevDup(boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ return cursor.getPrevDup(keyThang, primaryKeyThang, valueThang,
+ getLockMode(lockForWrite));
+ }
+
+ /**
+ * Binding version of Cursor.getSearchKey(), no join cursor allowed.
+ * Searches by record number in a BTREE-RECNO db with RECNO access.
+ */
+ OperationStatus getSearchKey(Object key, Object value,
+ boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ if (view.useKey(key, value, keyThang, range)) {
+ return doGetSearchKey(lockForWrite);
+ } else {
+ return OperationStatus.NOTFOUND;
+ }
+ }
+
+ /**
+ * Pass-thru version of Cursor.getSearchKey().
+ * Searches by record number in a BTREE-RECNO db with RECNO access.
+ */
+ private OperationStatus doGetSearchKey(boolean lockForWrite)
+ throws DatabaseException {
+
+ LockMode lockMode = getLockMode(lockForWrite);
+ if (view.btreeRecNumAccess) {
+ return cursor.getSearchRecordNumber(keyThang, primaryKeyThang,
+ valueThang, lockMode);
+ } else {
+ return cursor.getSearchKey(keyThang, primaryKeyThang,
+ valueThang, lockMode);
+ }
+ }
+
+ /**
+ * Binding version of Cursor.getSearchKeyRange(), no join cursor allowed.
+ */
+ OperationStatus getSearchKeyRange(Object key, Object value,
+ boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ if (view.useKey(key, value, keyThang, range)) {
+ return cursor.getSearchKeyRange(keyThang, primaryKeyThang,
+ valueThang,
+ getLockMode(lockForWrite));
+ } else {
+ return OperationStatus.NOTFOUND;
+ }
+ }
+
+ /**
+ * Binding version of Cursor.getSearchBoth(), no join cursor allowed.
+ * Unlike SecondaryCursor.getSearchBoth, for a secondary this searches for
+ * the primary value not the primary key.
+ */
+ OperationStatus getSearchBoth(Object key, Object value,
+ boolean lockForWrite)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ LockMode lockMode = getLockMode(lockForWrite);
+ view.useValue(value, valueThang, null);
+ if (view.useKey(key, value, keyThang, range)) {
+ if (view.isSecondary()) {
+ if (otherThang == null) {
+ otherThang = new DatabaseEntry();
+ }
+ OperationStatus status = cursor.getSearchKey(keyThang,
+ primaryKeyThang,
+ otherThang,
+ lockMode);
+ while (status == OperationStatus.SUCCESS) {
+ if (KeyRange.equalBytes(otherThang, valueThang)) {
+ break;
+ }
+ status = cursor.getNextDup(keyThang, primaryKeyThang,
+ otherThang, lockMode);
+ }
+ /* if status != SUCCESS set range cursor to invalid? */
+ return status;
+ } else {
+ return cursor.getSearchBoth(keyThang, null, valueThang,
+ lockMode);
+ }
+ } else {
+ return OperationStatus.NOTFOUND;
+ }
+ }
+
+ /**
+ * Find the given value using getSearchBoth if possible or a sequential
+ * scan otherwise, no join cursor allowed.
+ */
+ OperationStatus find(Object value, boolean findFirst)
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ LockMode lockMode = getLockMode(false);
+
+ if (view.entityBinding != null && !view.isSecondary() &&
+ (findFirst || !view.dupsAllowed)) {
+ return getSearchBoth(null, value, false);
+ } else {
+ if (otherThang == null) {
+ otherThang = new DatabaseEntry();
+ }
+ view.useValue(value, otherThang, null);
+ OperationStatus status = findFirst ? getFirst(false)
+ : getLast(false);
+ while (status == OperationStatus.SUCCESS) {
+ if (KeyRange.equalBytes(valueThang, otherThang)) {
+ break;
+ }
+ status = findFirst ? getNext(false) : getPrev(false);
+ }
+ return status;
+ }
+ }
+
+ /**
+ * Calls Cursor.count(), no join cursor allowed.
+ */
+ int count()
+ throws DatabaseException {
+
+ checkNoJoinCursor();
+ return cursor.count();
+ }
+
+ /**
+ * Binding version of Cursor.putCurrent().
+ */
+ OperationStatus putCurrent(Object value)
+ throws DatabaseException {
+
+ checkWriteAllowed(false);
+ view.useValue(value, valueThang, keyThang);
+
+ /*
+ * Workaround for a DB core problem: With HASH type a put() with
+ * different data is allowed.
+ */
+ boolean hashWorkaround = (view.dupsOrdered && !view.ordered);
+ if (hashWorkaround) {
+ if (otherThang == null) {
+ otherThang = new DatabaseEntry();
+ }
+ cursor.getCurrent(keyThang, primaryKeyThang, otherThang,
+ LockMode.DEFAULT);
+ if (KeyRange.equalBytes(valueThang, otherThang)) {
+ return OperationStatus.SUCCESS;
+ } else {
+ throw new IllegalArgumentException(
+ "Current data differs from put data with sorted duplicates");
+ }
+ }
+
+ return cursor.putCurrent(valueThang);
+ }
+
+ /**
+ * Binding version of Cursor.putAfter().
+ */
+ OperationStatus putAfter(Object value)
+ throws DatabaseException {
+
+ checkWriteAllowed(false);
+ view.useValue(value, valueThang, null); /* why no key check? */
+ return cursor.putAfter(valueThang);
+ }
+
+ /**
+ * Binding version of Cursor.putBefore().
+ */
+ OperationStatus putBefore(Object value)
+ throws DatabaseException {
+
+ checkWriteAllowed(false);
+ view.useValue(value, valueThang, keyThang);
+ return cursor.putBefore(valueThang);
+ }
+
+ /**
+ * Binding version of Cursor.put(), optionally returning the old value and
+ * optionally using the current key instead of the key parameter.
+ */
+ OperationStatus put(Object key, Object value, Object[] oldValue,
+ boolean useCurrentKey)
+ throws DatabaseException {
+
+ initForPut(key, value, oldValue, useCurrentKey);
+ return cursor.put(keyThang, valueThang);
+ }
+
+ /**
+ * Binding version of Cursor.putNoOverwrite(), optionally using the current
+ * key instead of the key parameter.
+ */
+ OperationStatus putNoOverwrite(Object key, Object value,
+ boolean useCurrentKey)
+ throws DatabaseException {
+
+ initForPut(key, value, null, useCurrentKey);
+ return cursor.putNoOverwrite(keyThang, valueThang);
+ }
+
+ /**
+ * Binding version of Cursor.putNoDupData(), optionally returning the old
+ * value and optionally using the current key instead of the key parameter.
+ */
+ OperationStatus putNoDupData(Object key, Object value, Object[] oldValue,
+ boolean useCurrentKey)
+ throws DatabaseException {
+
+ initForPut(key, value, oldValue, useCurrentKey);
+ if (view.dupsOrdered) {
+ return cursor.putNoDupData(keyThang, valueThang);
+ } else {
+ if (view.dupsAllowed) {
+ /* Unordered duplicates. */
+ OperationStatus status =
+ cursor.getSearchBoth(keyThang, primaryKeyThang,
+ valueThang,
+ getLockMode(false));
+ if (status == OperationStatus.SUCCESS) {
+ return OperationStatus.KEYEXIST;
+ } else {
+ return cursor.put(keyThang, valueThang);
+ }
+ } else {
+ /* No duplicates. */
+ return cursor.putNoOverwrite(keyThang, valueThang);
+ }
+ }
+ }
+
+ /**
+ * Do setup for a put() operation.
+ */
+ private void initForPut(Object key, Object value, Object[] oldValue,
+ boolean useCurrentKey)
+ throws DatabaseException {
+
+ checkWriteAllowed(false);
+ if (!useCurrentKey && !view.useKey(key, value, keyThang, range)) {
+ throw new IllegalArgumentException("key out of range");
+ }
+ if (oldValue != null) {
+ oldValue[0] = null;
+ if (!view.dupsAllowed) {
+ OperationStatus status = doGetSearchKey(true);
+ if (status == OperationStatus.SUCCESS) {
+ oldValue[0] = getCurrentValue();
+ }
+ }
+ }
+ view.useValue(value, valueThang, keyThang);
+ }
+
+ /**
+ * Sets the key entry to the begin key of a single key range, so the next
+ * time a putXxx() method is called that key will be used.
+ */
+ void useRangeKey() {
+ if (!range.singleKey) {
+ throw new IllegalStateException();
+ }
+ KeyRange.copy(range.beginKey, keyThang);
+ }
+
+ /**
+ * Perform an arbitrary database 'delete' operation.
+ */
+ OperationStatus delete()
+ throws DatabaseException {
+
+ checkWriteAllowed(true);
+ return cursor.delete();
+ }
+
+ /**
+ * Returns the lock mode to use for a getXxx() operation.
+ */
+ LockMode getLockMode(boolean lockForWrite) {
+
+ /* Dirty-read takes precedence over write-locking. */
+
+ if (dirtyRead) {
+ return LockMode.DIRTY_READ;
+ } else if (lockForWrite && !view.currentTxn.isDirtyRead()) {
+ return view.currentTxn.getWriteLockMode();
+ } else {
+ return LockMode.DEFAULT;
+ }
+ }
+
+ /**
+ * Throws an exception if a join cursor is in use.
+ */
+ private void checkNoJoinCursor() {
+
+ if (joinCursor != null) {
+ throw new UnsupportedOperationException
+ ("Not allowed with a join cursor");
+ }
+ }
+
+ /**
+ * Throws an exception if write is not allowed or if a join cursor is in
+ * use.
+ */
+ private void checkWriteAllowed(boolean allowSecondary) {
+
+ checkNoJoinCursor();
+
+ if (!writeAllowed || (!allowSecondary && view.isSecondary())) {
+ throw new UnsupportedOperationException
+ ("Writing is not allowed");
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/DataView.java b/db/java/src/com/sleepycat/collections/DataView.java
new file mode 100644
index 000000000..e8cec80b3
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/DataView.java
@@ -0,0 +1,598 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DataView.java,v 1.4 2004/08/02 18:52:05 mjc Exp $
+ */
+
+package com.sleepycat.collections;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.JoinConfig;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.db.SecondaryKeyCreator;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * Represents a Berkeley DB database and adds support for indices, bindings and
+ * key ranges.
+ *
+ * <p>This class defines a view and takes care of reading and updating indices,
+ * calling bindings, constraining access to a key range, etc.</p>
+ *
+ * @author Mark Hayes
+ */
+final class DataView implements Cloneable {
+
+ Database db;
+ SecondaryDatabase secDb;
+ CurrentTransaction currentTxn;
+ KeyRange range;
+ EntryBinding keyBinding;
+ EntryBinding valueBinding;
+ EntityBinding entityBinding;
+ PrimaryKeyAssigner keyAssigner;
+ SecondaryKeyCreator secKeyCreator;
+ boolean writeAllowed; // Read-write view
+ boolean ordered; // Not a HASH Db
+ boolean recNumAllowed; // QUEUE, RECNO, or BTREE-RECNUM Db
+ boolean recNumAccess; // recNumAllowed && using a rec num binding
+ boolean btreeRecNumDb; // BTREE-RECNUM Db
+ boolean btreeRecNumAccess; // recNumAccess && BTREE-RECNUM Db
+ boolean recNumRenumber; // RECNO-RENUM Db
+ boolean keysRenumbered; // recNumRenumber || btreeRecNumAccess
+ boolean dupsAllowed; // Dups configured
+ boolean dupsOrdered; // Sorted dups configured
+ boolean transactional; // Db is transactional
+ boolean dirtyReadAllowed; // Dirty-read is optional in DB-CORE
+ boolean dirtyReadEnabled; // This view is a dirty-ready view
+
+ /**
+ * Creates a view for a given database and bindings. The initial key range
+ * of the view will be open.
+ */
+ DataView(Database database, EntryBinding keyBinding,
+ EntryBinding valueBinding, EntityBinding entityBinding,
+ boolean writeAllowed, PrimaryKeyAssigner keyAssigner)
+ throws IllegalArgumentException {
+
+ if (database == null) {
+ throw new IllegalArgumentException("database is null");
+ }
+ db = database;
+ try {
+ currentTxn =
+ CurrentTransaction.getInstanceInternal(db.getEnvironment());
+ DatabaseConfig dbConfig;
+ if (db instanceof SecondaryDatabase) {
+ secDb = (SecondaryDatabase) database;
+ SecondaryConfig secConfig = secDb.getSecondaryConfig();
+ secKeyCreator = secConfig.getKeyCreator();
+ dbConfig = secConfig;
+ } else {
+ dbConfig = db.getConfig();
+ }
+ ordered = !DbCompat.isTypeHash(dbConfig);
+ recNumAllowed = DbCompat.isTypeQueue(dbConfig) ||
+ DbCompat.isTypeRecno(dbConfig) ||
+ DbCompat.getBtreeRecordNumbers(dbConfig);
+ recNumRenumber = DbCompat.getRenumbering(dbConfig);
+ dupsAllowed = DbCompat.getSortedDuplicates(dbConfig) ||
+ DbCompat.getUnsortedDuplicates(dbConfig);
+ dupsOrdered = DbCompat.getSortedDuplicates(dbConfig);
+ transactional = currentTxn.isTxnMode() &&
+ dbConfig.getTransactional();
+ dirtyReadAllowed = DbCompat.getDirtyRead(dbConfig);
+ btreeRecNumDb = recNumAllowed && DbCompat.isTypeBtree(dbConfig);
+ range = new KeyRange(dbConfig.getBtreeComparator());
+ } catch (DatabaseException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ this.writeAllowed = writeAllowed;
+ this.keyBinding = keyBinding;
+ this.valueBinding = valueBinding;
+ this.entityBinding = entityBinding;
+ this.keyAssigner = keyAssigner;
+
+ if (valueBinding != null && entityBinding != null)
+ throw new IllegalArgumentException(
+ "both valueBinding and entityBinding are non-null");
+
+ if (keyBinding instanceof com.sleepycat.bind.RecordNumberBinding) {
+ if (!recNumAllowed) {
+ throw new IllegalArgumentException(
+ "RecordNumberBinding requires DB_BTREE/DB_RECNUM, " +
+ "DB_RECNO, or DB_QUEUE");
+ }
+ recNumAccess = true;
+ if (btreeRecNumDb) {
+ btreeRecNumAccess = true;
+ }
+ }
+ keysRenumbered = recNumRenumber || btreeRecNumAccess;
+ }
+
+ /**
+ * Clones the view.
+ */
+ private DataView cloneView() {
+
+ try {
+ return (DataView) super.clone();
+ } catch (CloneNotSupportedException willNeverOccur) {
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Return a new key-set view derived from this view by setting the
+ * entity and value binding to null.
+ *
+ * @return the derived view.
+ */
+ DataView keySetView() {
+
+ if (keyBinding == null) {
+ throw new UnsupportedOperationException("must have keyBinding");
+ }
+ DataView view = cloneView();
+ view.valueBinding = null;
+ view.entityBinding = null;
+ return view;
+ }
+
+ /**
+ * Return a new value-set view derived from this view by setting the
+ * key binding to null.
+ *
+ * @return the derived view.
+ */
+ DataView valueSetView() {
+
+ if (valueBinding == null && entityBinding == null) {
+ throw new UnsupportedOperationException(
+ "must have valueBinding or entityBinding");
+ }
+ DataView view = cloneView();
+ view.keyBinding = null;
+ return view;
+ }
+
+ /**
+ * Return a new value-set view for single key range.
+ *
+ * @param singleKey the single key value.
+ *
+ * @return the derived view.
+ *
+ * @throws DatabaseException if a database problem occurs.
+ *
+ * @throws KeyRangeException if the specified range is not within the
+ * current range.
+ */
+ DataView valueSetView(Object singleKey)
+ throws DatabaseException, KeyRangeException {
+
+ /*
+ * Must do subRange before valueSetView since the latter clears the
+ * key binding needed for the former.
+ */
+ KeyRange singleKeyRange = subRange(singleKey);
+ DataView view = valueSetView();
+ view.range = singleKeyRange;
+ return view;
+ }
+
+ /**
+ * Return a new value-set view for key range, optionally changing
+ * the key binding.
+ */
+ DataView subView(Object beginKey, boolean beginInclusive,
+ Object endKey, boolean endInclusive,
+ EntryBinding keyBinding)
+ throws DatabaseException, KeyRangeException {
+
+ DataView view = cloneView();
+ view.setRange(beginKey, beginInclusive, endKey, endInclusive);
+ if (keyBinding != null) view.keyBinding = keyBinding;
+ return view;
+ }
+
+ /**
+ * Returns a new view with a specified dirtyRead setting.
+ */
+ DataView dirtyReadView(boolean enable) {
+
+ if (!dirtyReadAllowed)
+ return this;
+ DataView view = cloneView();
+ view.dirtyReadEnabled = enable;
+ return view;
+ }
+
+ /**
+ * Returns the current transaction for the view or null if the environment
+ * is non-transactional.
+ */
+ CurrentTransaction getCurrentTxn() {
+
+ return transactional ? currentTxn : null;
+ }
+
+ /**
+ * Sets this view's range to a subrange with the given parameters.
+ */
+ private void setRange(Object beginKey, boolean beginInclusive,
+ Object endKey, boolean endInclusive)
+ throws DatabaseException, KeyRangeException {
+
+ range = subRange(beginKey, beginInclusive, endKey, endInclusive);
+ }
+
+ /**
+ * Returns the key thang for a single key range, or null if a single key
+ * range is not used.
+ */
+ DatabaseEntry getSingleKeyThang() {
+
+ return range.getSingleKey();
+ }
+
+ /**
+ * Returns the environment for the database.
+ */
+ final Environment getEnv() {
+
+ return currentTxn.getEnvironment();
+ }
+
+ /**
+ * Returns whether this is a view on a secondary database rather
+ * than directly on a primary database.
+ */
+ final boolean isSecondary() {
+
+ return (secDb != null);
+ }
+
+ /**
+ * Returns whether no records are present in the view.
+ */
+ boolean isEmpty()
+ throws DatabaseException {
+
+ DataCursor cursor = new DataCursor(this, false);
+ try {
+ return cursor.getFirst(false) != OperationStatus.SUCCESS;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Appends a value and returns the new key. If a key assigner is used
+ * it assigns the key, otherwise a QUEUE or RECNO database is required.
+ */
+ OperationStatus append(Object value, Object[] retPrimaryKey,
+ Object[] retValue)
+ throws DatabaseException {
+
+ /*
+ * Flags will be NOOVERWRITE if used with assigner, or APPEND
+ * otherwise.
+ * Requires: if value param, value or entity binding
+ * Requires: if retPrimaryKey, primary key binding (no index).
+ * Requires: if retValue, value or entity binding
+ */
+ DatabaseEntry keyThang = new DatabaseEntry();
+ DatabaseEntry valueThang = new DatabaseEntry();
+ useValue(value, valueThang, null);
+ OperationStatus status;
+ if (keyAssigner != null) {
+ keyAssigner.assignKey(keyThang);
+ if (!range.check(keyThang)) {
+ throw new IllegalArgumentException(
+ "assigned key out of range");
+ }
+ DataCursor cursor = new DataCursor(this, true);
+ try {
+ status = cursor.getCursor().putNoOverwrite(keyThang,
+ valueThang);
+ } finally {
+ cursor.close();
+ }
+ } else {
+ /* Assume QUEUE/RECNO access method. */
+ if (currentTxn.isCDBCursorOpen(db)) {
+ throw new IllegalStateException(
+ "cannot open CDB write cursor when read cursor is open");
+ }
+ status = DbCompat.append(db, useTransaction(),
+ keyThang, valueThang);
+ if (status == OperationStatus.SUCCESS && !range.check(keyThang)) {
+ db.delete(useTransaction(), keyThang);
+ throw new IllegalArgumentException(
+ "appended record number out of range");
+ }
+ }
+ if (status == OperationStatus.SUCCESS) {
+ returnPrimaryKeyAndValue(keyThang, valueThang,
+ retPrimaryKey, retValue);
+ }
+ return status;
+ }
+
+ /**
+ * Returns the current transaction if the database is transaction, or null
+ * if the database is not transactional or there is no current transaction.
+ */
+ Transaction useTransaction() {
+ return transactional ? currentTxn.getTransaction() : null;
+ }
+
+ /**
+ * Deletes all records in the current range.
+ */
+ void clear()
+ throws DatabaseException {
+
+ DataCursor cursor = new DataCursor(this, true);
+ try {
+ OperationStatus status = OperationStatus.SUCCESS;
+ while (status == OperationStatus.SUCCESS) {
+ if (keysRenumbered) {
+ status = cursor.getFirst(true);
+ } else {
+ status = cursor.getNext(true);
+ }
+ if (status == OperationStatus.SUCCESS) {
+ cursor.delete();
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Returns a cursor for this view that reads only records having the
+ * specified index key values.
+ */
+ DataCursor join(DataView[] indexViews, Object[] indexKeys,
+ JoinConfig joinConfig)
+ throws DatabaseException {
+
+ DataCursor joinCursor = null;
+ DataCursor[] indexCursors = new DataCursor[indexViews.length];
+ try {
+ for (int i = 0; i < indexViews.length; i += 1) {
+ indexCursors[i] = new DataCursor(indexViews[i], false);
+ indexCursors[i].getSearchKey(indexKeys[i], null, false);
+ }
+ joinCursor = new DataCursor(this, indexCursors, joinConfig, true);
+ return joinCursor;
+ } finally {
+ if (joinCursor == null) {
+ // An exception is being thrown, so close cursors we opened.
+ for (int i = 0; i < indexCursors.length; i += 1) {
+ if (indexCursors[i] != null) {
+ try { indexCursors[i].close(); }
+ catch (Exception e) {
+ /* FindBugs, this is ok. */
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a cursor for this view that reads only records having the
+ * index key values at the specified cursors.
+ */
+ DataCursor join(DataCursor[] indexCursors, JoinConfig joinConfig)
+ throws DatabaseException {
+
+ return new DataCursor(this, indexCursors, joinConfig, false);
+ }
+
+ /**
+ * Returns primary key and value if return parameters are non-null.
+ */
+ private void returnPrimaryKeyAndValue(DatabaseEntry keyThang,
+ DatabaseEntry valueThang,
+ Object[] retPrimaryKey,
+ Object[] retValue)
+ throws DatabaseException {
+
+ // Requires: if retPrimaryKey, primary key binding (no index).
+ // Requires: if retValue, value or entity binding
+
+ if (retPrimaryKey != null) {
+ if (keyBinding == null) {
+ throw new IllegalArgumentException(
+ "returning key requires primary key binding");
+ } else if (isSecondary()) {
+ throw new IllegalArgumentException(
+ "returning key requires unindexed view");
+ } else {
+ retPrimaryKey[0] = keyBinding.entryToObject(keyThang);
+ }
+ }
+ if (retValue != null) {
+ retValue[0] = makeValue(keyThang, valueThang);
+ }
+ }
+
+ /**
+ * Populates the key entry and returns whether the key is within range.
+ */
+ boolean useKey(Object key, Object value, DatabaseEntry keyThang,
+ KeyRange checkRange)
+ throws DatabaseException {
+
+ if (key != null) {
+ if (keyBinding == null) {
+ throw new IllegalArgumentException(
+ "non-null key with null key binding");
+ }
+ keyBinding.objectToEntry(key, keyThang);
+ } else {
+ if (value == null) {
+ throw new IllegalArgumentException(
+ "null key and null value");
+ }
+ if (entityBinding == null) {
+ throw new IllegalStateException(
+ "EntityBinding required to derive key from value");
+ }
+ if (isSecondary()) {
+ DatabaseEntry primaryKeyThang = new DatabaseEntry();
+ entityBinding.objectToKey(value, primaryKeyThang);
+ DatabaseEntry valueThang = new DatabaseEntry();
+ entityBinding.objectToData(value, valueThang);
+ secKeyCreator.createSecondaryKey(secDb, primaryKeyThang,
+ valueThang, keyThang);
+ } else {
+ entityBinding.objectToKey(value, keyThang);
+ }
+ }
+ if (recNumAccess && DbCompat.getRecordNumber(keyThang) <= 0) {
+ return false;
+ }
+ if (checkRange != null && !checkRange.check(keyThang)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns whether data keys can be derived from the value/entity binding
+ * of this view, which determines whether a value/entity object alone is
+ * sufficient for operations that require keys.
+ */
+ final boolean canDeriveKeyFromValue() {
+
+ return (entityBinding != null);
+ }
+
+ /**
+ * Populates the value entry and throws an exception if the primary key
+ * would be changed via an entity binding.
+ */
+ void useValue(Object value, DatabaseEntry valueThang,
+ DatabaseEntry checkKeyThang)
+ throws DatabaseException {
+
+ if (value != null) {
+ if (valueBinding != null) {
+ valueBinding.objectToEntry(value, valueThang);
+ } else if (entityBinding != null) {
+ entityBinding.objectToData(value, valueThang);
+ if (checkKeyThang != null) {
+ DatabaseEntry thang = new DatabaseEntry();
+ entityBinding.objectToKey(value, thang);
+ if (!KeyRange.equalBytes(thang, checkKeyThang)) {
+ throw new IllegalArgumentException(
+ "cannot change primary key");
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "non-null value with null value/entity binding");
+ }
+ } else {
+ valueThang.setData(new byte[0]);
+ valueThang.setOffset(0);
+ valueThang.setSize(0);
+ }
+ }
+
+ /**
+ * Converts a key entry to a key object.
+ */
+ Object makeKey(DatabaseEntry keyThang)
+ throws DatabaseException {
+
+ if (keyThang.getSize() == 0) return null;
+ return keyBinding.entryToObject(keyThang);
+ }
+
+ /**
+ * Converts a key-value entry pair to a value object.
+ */
+ Object makeValue(DatabaseEntry primaryKeyThang, DatabaseEntry valueThang)
+ throws DatabaseException {
+
+ Object value;
+ if (valueBinding != null) {
+ value = valueBinding.entryToObject(valueThang);
+ } else if (entityBinding != null) {
+ value = entityBinding.entryToObject(primaryKeyThang,
+ valueThang);
+ } else {
+ throw new UnsupportedOperationException(
+ "requires valueBinding or entityBinding");
+ }
+ return value;
+ }
+
+ /**
+ * Intersects the given key and the current range.
+ */
+ KeyRange subRange(Object singleKey)
+ throws DatabaseException, KeyRangeException {
+
+ return range.subRange(makeRangeKey(singleKey));
+ }
+
+ /**
+ * Intersects the given range and the current range.
+ */
+ KeyRange subRange(Object beginKey, boolean beginInclusive,
+ Object endKey, boolean endInclusive)
+ throws DatabaseException, KeyRangeException {
+
+ if (beginKey == endKey && beginInclusive && endInclusive) {
+ return subRange(beginKey);
+ }
+ if (!ordered) {
+ throw new UnsupportedOperationException(
+ "Cannot use key ranges on an unsorted database");
+ }
+ DatabaseEntry beginThang =
+ (beginKey != null) ? makeRangeKey(beginKey) : null;
+ DatabaseEntry endThang =
+ (endKey != null) ? makeRangeKey(endKey) : null;
+
+ return range.subRange(beginThang, beginInclusive,
+ endThang, endInclusive);
+ }
+
+ /**
+ * Given a key object, make a key entry that can be used in a range.
+ */
+ private DatabaseEntry makeRangeKey(Object key)
+ throws DatabaseException {
+
+ DatabaseEntry thang = new DatabaseEntry();
+ if (keyBinding != null) {
+ useKey(key, null, thang, null);
+ } else {
+ useKey(null, key, thang, null);
+ }
+ return thang;
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/KeyRange.java b/db/java/src/com/sleepycat/collections/KeyRange.java
new file mode 100644
index 000000000..8a5b7d3b2
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/KeyRange.java
@@ -0,0 +1,299 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: KeyRange.java,v 1.2 2004/05/05 15:43:48 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Comparator;
+
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * Encapsulates a key range for use with a RangeCursor.
+ */
+class KeyRange {
+
+ Comparator comparator;
+ DatabaseEntry beginKey;
+ DatabaseEntry endKey;
+ boolean singleKey;
+ boolean beginInclusive;
+ boolean endInclusive;
+
+ /**
+ * Creates an unconstrained key range.
+ */
+ KeyRange(Comparator comparator) {
+ this.comparator = comparator;
+ }
+
+ /**
+ * Creates a range for a single key.
+ */
+ KeyRange subRange(DatabaseEntry key)
+ throws KeyRangeException {
+
+ if (!check(key)) {
+ throw new KeyRangeException("singleKey out of range");
+ }
+ KeyRange range = new KeyRange(comparator);
+ range.beginKey = key;
+ range.endKey = key;
+ range.beginInclusive = true;
+ range.endInclusive = true;
+ range.singleKey = true;
+ return range;
+ }
+
+ /**
+ * Creates a range that is the intersection of this range and the given
+ * range parameters.
+ */
+ KeyRange subRange(DatabaseEntry beginKey, boolean beginInclusive,
+ DatabaseEntry endKey, boolean endInclusive)
+ throws KeyRangeException {
+
+ if (beginKey == null) {
+ beginKey = this.beginKey;
+ beginInclusive = this.beginInclusive;
+ } else if (!check(beginKey, beginInclusive)) {
+ throw new KeyRangeException("beginKey out of range");
+ }
+ if (endKey == null) {
+ endKey = this.endKey;
+ endInclusive = this.endInclusive;
+ } else if (!check(endKey, endInclusive)) {
+ throw new KeyRangeException("endKey out of range");
+ }
+ KeyRange range = new KeyRange(comparator);
+ range.beginKey = beginKey;
+ range.endKey = endKey;
+ range.beginInclusive = beginInclusive;
+ range.endInclusive = endInclusive;
+ return range;
+ }
+
+ /**
+ * Returns the key of a single-key range, or null if not a single-key
+ * range.
+ */
+ final DatabaseEntry getSingleKey() {
+
+ return singleKey ? beginKey : null;
+ }
+
+ /**
+ * Returns whether this range has a begin or end bound.
+ */
+ final boolean hasBound() {
+
+ return endKey != null || beginKey != null;
+ }
+
+ /**
+ * Formats this range as a string for debugging.
+ */
+ public String toString() {
+
+ return "[KeyRange " + beginKey + ' ' + beginInclusive +
+ endKey + ' ' + endInclusive +
+ (singleKey ? " single" : "");
+ }
+
+ /**
+ * Returns whether a given key is within range.
+ */
+ boolean check(DatabaseEntry key) {
+
+ if (singleKey) {
+ return (compare(key, beginKey) == 0);
+ } else {
+ return checkBegin(key, true) && checkEnd(key, true);
+ }
+ }
+
+ /**
+ * Returns whether a given key is within range.
+ */
+ boolean check(DatabaseEntry key, boolean inclusive) {
+
+ if (singleKey) {
+ return (compare(key, beginKey) == 0);
+ } else {
+ return checkBegin(key, inclusive) && checkEnd(key, inclusive);
+ }
+ }
+
+ /**
+ * Returns whether the given key is within range with respect to the
+ * beginning of the range.
+ *
+ * <p>The inclusive parameter should be true for checking a key read from
+ * the database; this will require that the key is within range. When
+ * inclusive=false the key is allowed to be equal to the beginKey for the
+ * range; this is used for checking a new exclusive bound of a
+ * sub-range.</p>
+ *
+ * <p>Note that when inclusive=false and beginInclusive=true our check is
+ * not exactly correct because in theory we should allow the key to be "one
+ * less" than the existing bound; however, checking for "one less" is
+ * impossible so we do the best we can and test the bounds
+ * conservatively.</p>
+ */
+ boolean checkBegin(DatabaseEntry key, boolean inclusive) {
+
+ if (beginKey == null) {
+ return true;
+ } else if (!beginInclusive && inclusive) {
+ return compare(key, beginKey) > 0;
+ } else {
+ return compare(key, beginKey) >= 0;
+ }
+ }
+
+ /**
+ * Returns whether the given key is within range with respect to the
+ * end of the range. See checkBegin for details.
+ */
+ boolean checkEnd(DatabaseEntry key, boolean inclusive) {
+
+ if (endKey == null) {
+ return true;
+ } else if (!endInclusive && inclusive) {
+ return compare(key, endKey) < 0;
+ } else {
+ return compare(key, endKey) <= 0;
+ }
+ }
+
+ /**
+ * Compares two keys, using the user comparator if there is one.
+ */
+ int compare(DatabaseEntry key1, DatabaseEntry key2) {
+
+ if (comparator != null) {
+ return comparator.compare(getByteArray(key1), getByteArray(key2));
+ } else {
+ return compareBytes
+ (key1.getData(), key1.getOffset(), key1.getSize(),
+ key2.getData(), key2.getOffset(), key2.getSize());
+
+ }
+ }
+
+ /**
+ * Compares two keys as unsigned byte arrays, which is the default
+ * comparison used by JE/DB.
+ */
+ static int compareBytes(byte[] data1, int offset1, int size1,
+ byte[] data2, int offset2, int size2) {
+
+ for (int i = 0; i < size1 && i < size2; i++) {
+
+ int b1 = 0xFF & data1[offset1 + i];
+ int b2 = 0xFF & data2[offset2 + i];
+ if (b1 < b2)
+ return -1;
+ else if (b1 > b2)
+ return 1;
+ }
+
+ if (size1 < size2)
+ return -1;
+ else if (size1 > size2)
+ return 1;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns a copy of an entry.
+ */
+ static DatabaseEntry copy(DatabaseEntry from) {
+ return new DatabaseEntry(getByteArray(from));
+ }
+
+ /**
+ * Copies one entry to another.
+ */
+ static void copy(DatabaseEntry from, DatabaseEntry to) {
+ to.setData(getByteArray(from));
+ to.setOffset(0);
+ }
+
+ /**
+ * Returns an entry's byte array, copying it if the entry offset is
+ * non-zero.
+ */
+ static byte[] getByteArray(DatabaseEntry entry) {
+
+ byte[] bytes = entry.getData();
+ if (bytes == null) return null;
+ int size = entry.getSize();
+ byte[] data = new byte[size];
+ System.arraycopy(bytes, entry.getOffset(), data, 0, size);
+ return data;
+ }
+
+ /**
+ * Returns the two DatabaseEntry objects have the same data value.
+ */
+ static boolean equalBytes(DatabaseEntry e1, DatabaseEntry e2) {
+
+ if (e1 == null && e2 == null) {
+ return true;
+ }
+ if (e1 == null || e2 == null) {
+ return false;
+ }
+
+ byte[] d1 = e1.getData();
+ byte[] d2 = e2.getData();
+ int s1 = e1.getSize();
+ int s2 = e2.getSize();
+ int o1 = e1.getOffset();
+ int o2 = e2.getOffset();
+
+ if (d1 == null && d2 == null) {
+ return true;
+ }
+ if (d1 == null || d2 == null) {
+ return false;
+ }
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i = 0; i < s1; i += 1) {
+ if (d1[o1 + i] != d2[o2 + i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Converts the byte array of this thang to space-separated integers,
+ * and suffixed by the record number if applicable.
+ *
+ * @param dbt the thang to convert.
+ *
+ * @param the resulting string.
+ */
+ static String toString(DatabaseEntry dbt) {
+
+ int len = dbt.getOffset() + dbt.getSize();
+ StringBuffer buf = new StringBuffer(len * 2);
+ byte[] data = dbt.getData();
+ for (int i = dbt.getOffset(); i < len; i++) {
+ String num = Integer.toHexString(data[i]);
+ if (num.length() < 2) buf.append('0');
+ buf.append(num);
+ }
+ return buf.toString();
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/KeyRangeException.java b/db/java/src/com/sleepycat/collections/KeyRangeException.java
new file mode 100644
index 000000000..342ebd528
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/KeyRangeException.java
@@ -0,0 +1,26 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: KeyRangeException.java,v 1.1 2004/04/09 16:34:08 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+/**
+ * An exception thrown when a key is out of range.
+ *
+ * @author Mark Hayes
+ */
+class KeyRangeException extends IllegalArgumentException {
+
+ /**
+ * Creates a key range exception.
+ */
+ public KeyRangeException(String msg) {
+
+ super(msg);
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/MapEntryParameter.java b/db/java/src/com/sleepycat/collections/MapEntryParameter.java
new file mode 100644
index 000000000..8148bbe8a
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/MapEntryParameter.java
@@ -0,0 +1,126 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: MapEntryParameter.java,v 1.1 2004/04/09 16:34:08 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Map;
+
+/**
+ * A simple <code>Map.Entry</code> implementation that can be used as in
+ * input parameter. Since a <code>MapEntryParameter</code> is not obtained
+ * from a map, it is not attached to any map in particular. To emphasize that
+ * changing this object does not change the map, the {@link #setValue} method
+ * always throws <code>UnsupportedOperationException</code>.
+ *
+ * <p><b>Warning:</b> Use of this interface violates the Java Collections
+ * interface contract since these state that <code>Map.Entry</code> objects
+ * should only be obtained from <code>Map.entrySet()</code> sets, while this
+ * class allows constructing them directly. However, it is useful for
+ * performing operations on an entry set such as add(), contains(), etc. For
+ * restrictions see {@link #getValue} and {@link #setValue}.</p>
+ *
+ * @author Mark Hayes
+ */
+public class MapEntryParameter implements Map.Entry {
+
+ private Object key;
+ private Object value;
+
+ /**
+ * Creates a map entry with a given key and value.
+ *
+ * @param key is the key to use.
+ *
+ * @param value is the value to use.
+ */
+ public MapEntryParameter(Object key, Object value) {
+
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * Computes a hash code as specified by {@link
+ * java.util.Map.Entry#hashCode}.
+ *
+ * @return the computed hash code.
+ */
+ public int hashCode() {
+
+ return ((key == null) ? 0 : key.hashCode()) ^
+ ((value == null) ? 0 : value.hashCode());
+ }
+
+ /**
+ * Compares this entry to a given entry as specified by {@link
+ * java.util.Map.Entry#equals}.
+ *
+ * @return the computed hash code.
+ */
+ public boolean equals(Object other) {
+
+ if (!(other instanceof Map.Entry)) {
+ return false;
+ }
+
+ Map.Entry e = (Map.Entry) other;
+
+ return ((key == null) ? (e.getKey() == null)
+ : key.equals(e.getKey())) &&
+ ((value == null) ? (e.getValue() == null)
+ : value.equals(e.getValue()));
+ }
+
+ /**
+ * Returns the key of this entry.
+ *
+ * @return the key of this entry.
+ */
+ public final Object getKey() {
+
+ return key;
+ }
+
+ /**
+ * Returns the value of this entry. Note that this will be the value
+ * passed to the constructor or the last value passed to {@link #setValue}.
+ * It will not reflect changes made to a Map.
+ *
+ * @return the value of this entry.
+ */
+ public final Object getValue() {
+
+ return value;
+ }
+
+ /**
+ * Always throws <code>UnsupportedOperationException</code> since this
+ * object is not attached to a map.
+ */
+ public Object setValue(Object newValue) {
+
+ throw new UnsupportedOperationException();
+ }
+
+ final void setValueInternal(Object newValue) {
+
+ this.value = newValue;
+ }
+
+ /**
+ * Converts the entry to a string representation for debugging.
+ *
+ * @return the string representation.
+ */
+ public String toString() {
+
+ return "[key [" + key + "] value [" + value + ']';
+ }
+}
+
diff --git a/db/java/src/com/sleepycat/collections/PrimaryKeyAssigner.java b/db/java/src/com/sleepycat/collections/PrimaryKeyAssigner.java
new file mode 100644
index 000000000..1af3ba1ae
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/PrimaryKeyAssigner.java
@@ -0,0 +1,30 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: PrimaryKeyAssigner.java,v 1.2 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * An interface implemented to assign new primary key values.
+ * An implementation of this interface is passed to the {@link StoredMap}
+ * or {@link StoredSortedMap} constructor to assign primary keys for that
+ * store. Key assignment occurs when <code>StoredMap.append()</code> is called.
+ *
+ * @author Mark Hayes
+ */
+public interface PrimaryKeyAssigner {
+
+ /**
+ * Assigns a new primary key value into the given data buffer.
+ */
+ void assignKey(DatabaseEntry keyData)
+ throws DatabaseException;
+}
diff --git a/db/java/src/com/sleepycat/collections/RangeCursor.java b/db/java/src/com/sleepycat/collections/RangeCursor.java
new file mode 100644
index 000000000..ae664ef51
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/RangeCursor.java
@@ -0,0 +1,874 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: RangeCursor.java,v 1.3 2004/09/22 18:01:02 bostic Exp $
+ */
+
+package com.sleepycat.collections;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.LockMode;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.SecondaryCursor;
+
+/**
+ * A cursor-like interface that enforces a key range. The method signatures
+ * are actually those of SecondaryCursor, but the pKey parameter may be null.
+ * It was done this way to avoid doubling the number of methods.
+ *
+ * <p>This is not a general implementation of a range cursor and should not
+ * be used outside this package; however, it may evolve into a generally useful
+ * range cursor some day.</p>
+ *
+ * @author Mark Hayes
+ */
+class RangeCursor implements Cloneable {
+
+ /**
+ * The cursor and secondary cursor are the same object. The secCursor is
+ * null if the database is not a secondary database.
+ */
+ private Cursor cursor;
+ private SecondaryCursor secCursor;
+ private CurrentTransaction currentTxn;
+ private boolean writeAllowed;
+
+ /**
+ * The range is always non-null, but may be unbounded meaning that it is
+ * open and not used.
+ */
+ private KeyRange range;
+
+ /**
+ * If the database is a RECNO or QUEUE database, we know its keys are
+ * record numbers. We treat a non-positive record number as out of bounds,
+ * that is, we return NOTFOUND rather than throwing
+ * IllegalArgumentException as would happen if we passed a non-positive
+ * record number into the DB cursor. This behavior is required by the
+ * collections interface.
+ */
+ private boolean isRecnoOrQueue;
+
+ /**
+ * The privXxx entries are used only when the range is bounded. We read
+ * into these private entries to avoid modifying the caller's entry
+ * parameters in the case where we read sucessfully but the key is out of
+ * range. In that case we return NOTFOUND and we want to leave the entry
+ * parameters unchanged.
+ */
+ private DatabaseEntry privKey;
+ private DatabaseEntry privPKey;
+ private DatabaseEntry privData;
+
+ /**
+ * The initialized flag is set to true whenever we sucessfully position the
+ * cursor. It is used to implement the getNext/Prev logic for doing a
+ * getFirst/Last when the cursor is not initialized. We can't rely on
+ * Cursor to do that for us, since if we position the underlying cursor
+ * sucessfully but the key is out of range, we have no way to set the
+ * underlying cursor to uninitialized. A range cursor always starts in
+ * the uninitialized state.
+ */
+ private boolean initialized;
+
+ /**
+ * Create a range cursor.
+ */
+ RangeCursor(DataView view, KeyRange range, boolean writeAllowed)
+ throws DatabaseException {
+
+ this.range = range;
+ this.writeAllowed = writeAllowed;
+ this.currentTxn = view.currentTxn;
+ isRecnoOrQueue = view.recNumAllowed && !view.btreeRecNumDb;
+
+ cursor = currentTxn.openCursor(view.db, writeAllowed,
+ view.useTransaction());
+ init();
+ }
+
+ /**
+ * Create a cloned range cursor. The caller must clone the underlying
+ * cursor before using this constructor, because cursor open/close is
+ * handled specially for CDS cursors outside this class.
+ */
+ RangeCursor dup(boolean samePosition)
+ throws DatabaseException {
+
+ try {
+ RangeCursor c = (RangeCursor) super.clone();
+ c.cursor = currentTxn.dupCursor(cursor, writeAllowed,
+ samePosition);
+ c.init();
+ return c;
+ } catch (CloneNotSupportedException neverHappens) {
+ return null;
+ }
+ }
+
+ /**
+ * Used for opening and duping (cloning).
+ */
+ private void init() {
+
+ if (cursor instanceof SecondaryCursor) {
+ secCursor = (SecondaryCursor) cursor;
+ } else {
+ secCursor = null;
+ }
+
+ if (range.hasBound()) {
+ privKey = new DatabaseEntry();
+ privPKey = new DatabaseEntry();
+ privData = new DatabaseEntry();
+ } else {
+ privKey = null;
+ privPKey = null;
+ privData = null;
+ }
+ }
+
+ /**
+ * Returns the underlying cursor. Used for cloning.
+ */
+ Cursor getCursor() {
+ return cursor;
+ }
+
+ /**
+ * When an unbounded range is used, this method is called to use the
+ * callers entry parameters directly, to avoid the extra step of copying
+ * between the private entries and the caller's entries.
+ */
+ private void setParams(DatabaseEntry key, DatabaseEntry pKey,
+ DatabaseEntry data) {
+ privKey = key;
+ privPKey = pKey;
+ privData = data;
+ }
+
+ /**
+ * Dups the cursor, sets the cursor and secCursor fields to the duped
+ * cursor, and returns the old cursor. Always call endOperation in a
+ * finally clause after calling beginOperation.
+ *
+ * <p>If the returned cursor == the cursor field, the cursor is
+ * uninitialized and was not duped; this case is handled correctly by
+ * endOperation.</p>
+ */
+ private Cursor beginOperation()
+ throws DatabaseException {
+
+ Cursor oldCursor = cursor;
+ if (initialized) {
+ cursor = currentTxn.dupCursor(cursor, writeAllowed, true);
+ if (secCursor != null) {
+ secCursor = (SecondaryCursor) cursor;
+ }
+ } else {
+ return cursor;
+ }
+ return oldCursor;
+ }
+
+ /**
+ * If the operation succeded, leaves the duped cursor in place and closes
+ * the oldCursor. If the operation failed, moves the oldCursor back in
+ * place and closes the duped cursor. oldCursor may be null if
+ * beginOperation was not called, in cases where we don't need to dup
+ * the cursor. Always call endOperation when a successful operation ends,
+ * in order to set the initialized field.
+ */
+ private void endOperation(Cursor oldCursor, OperationStatus status,
+ DatabaseEntry key, DatabaseEntry pKey,
+ DatabaseEntry data)
+ throws DatabaseException {
+
+ if (status == OperationStatus.SUCCESS) {
+ if (oldCursor != null && oldCursor != cursor) {
+ currentTxn.closeCursor(oldCursor);
+ }
+ if (key != null) {
+ swapData(key, privKey);
+ }
+ if (pKey != null && secCursor != null) {
+ swapData(pKey, privPKey);
+ }
+ if (data != null) {
+ swapData(data, privData);
+ }
+ initialized = true;
+ } else {
+ if (oldCursor != null && oldCursor != cursor) {
+ currentTxn.closeCursor(cursor);
+ cursor = oldCursor;
+ if (secCursor != null) {
+ secCursor = (SecondaryCursor) cursor;
+ }
+ }
+ }
+ }
+
+ /**
+ * Swaps the contents of the two entries. Used to return entry data to
+ * the caller when the operation was successful.
+ */
+ static private void swapData(DatabaseEntry e1, DatabaseEntry e2) {
+
+ byte[] d1 = e1.getData();
+ int o1 = e1.getOffset();
+ int s1 = e1.getSize();
+
+ e1.setData(e2.getData(), e2.getOffset(), e2.getSize());
+ e2.setData(d1, o1, s1);
+ }
+
+ /**
+ * Shares the same byte array, offset and size between two entries.
+ * Used when copying the entry data is not necessary because it is known
+ * that the underlying operation will not modify the entry, for example,
+ * with getSearchKey.
+ */
+ static private void shareData(DatabaseEntry from, DatabaseEntry to) {
+
+ if (from != null) {
+ to.setData(from.getData(), from.getOffset(), from.getSize());
+ }
+ }
+
+ public OperationStatus getFirst(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetFirst(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ if (range.singleKey) {
+ KeyRange.copy(range.beginKey, privKey);
+ status = doGetSearchKey(lockMode);
+ endOperation(null, status, key, pKey, data);
+ } else {
+ status = OperationStatus.NOTFOUND;
+ Cursor oldCursor = beginOperation();
+ try {
+ if (range.beginKey == null) {
+ status = doGetFirst(lockMode);
+ } else {
+ KeyRange.copy(range.beginKey, privKey);
+ status = doGetSearchKeyRange(lockMode);
+ if (status == OperationStatus.SUCCESS &&
+ !range.beginInclusive &&
+ range.compare(privKey, range.beginKey) == 0) {
+ status = doGetNext(lockMode);
+ }
+ }
+ if (status == OperationStatus.SUCCESS &&
+ !range.check(privKey)) {
+ status = OperationStatus.NOTFOUND;
+ }
+ } finally {
+ endOperation(oldCursor, status, key, pKey, data);
+ }
+ }
+ return status;
+ }
+
+ public OperationStatus getLast(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status = OperationStatus.NOTFOUND;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetLast(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ Cursor oldCursor = beginOperation();
+ try {
+ if (range.endKey == null) {
+ status = doGetLast(lockMode);
+ } else {
+ KeyRange.copy(range.endKey, privKey);
+ status = doGetSearchKeyRange(lockMode);
+ if (status == OperationStatus.SUCCESS) {
+ if (range.endInclusive &&
+ range.compare(range.endKey, privKey) == 0) {
+ status = doGetNextNoDup(lockMode);
+ if (status == OperationStatus.SUCCESS) {
+ status = doGetPrev(lockMode);
+ } else {
+ status = doGetLast(lockMode);
+ }
+ } else {
+ status = doGetPrev(lockMode);
+ }
+ } else {
+ status = doGetLast(lockMode);
+ }
+ }
+ if (status == OperationStatus.SUCCESS &&
+ !range.checkBegin(privKey, true)) {
+ status = OperationStatus.NOTFOUND;
+ }
+ } finally {
+ endOperation(oldCursor, status, key, pKey, data);
+ }
+ return status;
+ }
+
+ public OperationStatus getNext(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status;
+ if (!initialized) {
+ return getFirst(key, pKey, data, lockMode);
+ }
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetNext(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ if (range.singleKey) {
+ status = doGetNextDup(lockMode);
+ endOperation(null, status, key, pKey, data);
+ } else {
+ status = OperationStatus.NOTFOUND;
+ Cursor oldCursor = beginOperation();
+ try {
+ status = doGetNext(lockMode);
+ if (status == OperationStatus.SUCCESS &&
+ !range.check(privKey)) {
+ status = OperationStatus.NOTFOUND;
+ }
+ } finally {
+ endOperation(oldCursor, status, key, pKey, data);
+ }
+ }
+ return status;
+ }
+
+ public OperationStatus getNextNoDup(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status;
+ if (!initialized) {
+ return getFirst(key, pKey, data, lockMode);
+ }
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetNextNoDup(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ if (range.singleKey) {
+ status = OperationStatus.NOTFOUND;
+ } else {
+ status = OperationStatus.NOTFOUND;
+ Cursor oldCursor = beginOperation();
+ try {
+ status = doGetNextNoDup(lockMode);
+ if (status == OperationStatus.SUCCESS &&
+ !range.check(privKey)) {
+ status = OperationStatus.NOTFOUND;
+ }
+ } finally {
+ endOperation(oldCursor, status, key, pKey, data);
+ }
+ }
+ return status;
+ }
+
+ public OperationStatus getPrev(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status;
+ if (!initialized) {
+ return getLast(key, pKey, data, lockMode);
+ }
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetPrev(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ if (range.singleKey) {
+ status = doGetPrevDup(lockMode);
+ endOperation(null, status, key, pKey, data);
+ } else {
+ status = OperationStatus.NOTFOUND;
+ Cursor oldCursor = beginOperation();
+ try {
+ status = doGetPrev(lockMode);
+ if (status == OperationStatus.SUCCESS &&
+ !range.check(privKey)) {
+ status = OperationStatus.NOTFOUND;
+ }
+ } finally {
+ endOperation(oldCursor, status, key, pKey, data);
+ }
+ }
+ return status;
+ }
+
+ public OperationStatus getPrevNoDup(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status;
+ if (!initialized) {
+ return getLast(key, pKey, data, lockMode);
+ }
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetPrevNoDup(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ if (range.singleKey) {
+ status = OperationStatus.NOTFOUND;
+ } else {
+ status = OperationStatus.NOTFOUND;
+ Cursor oldCursor = beginOperation();
+ try {
+ status = doGetPrevNoDup(lockMode);
+ if (status == OperationStatus.SUCCESS &&
+ !range.check(privKey)) {
+ status = OperationStatus.NOTFOUND;
+ }
+ } finally {
+ endOperation(oldCursor, status, key, pKey, data);
+ }
+ }
+ return status;
+ }
+
+ public OperationStatus getSearchKey(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetSearchKey(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ if (!range.check(key)) {
+ status = OperationStatus.NOTFOUND;
+ } else {
+ shareData(key, privKey);
+ status = doGetSearchKey(lockMode);
+ endOperation(null, status, key, pKey, data);
+ }
+ return status;
+ }
+
+ public OperationStatus getSearchBoth(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetSearchBoth(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ if (!range.check(key)) {
+ status = OperationStatus.NOTFOUND;
+ } else {
+ shareData(key, privKey);
+ if (secCursor != null) {
+ shareData(pKey, privPKey);
+ } else {
+ shareData(data, privData);
+ }
+ status = doGetSearchBoth(lockMode);
+ endOperation(null, status, key, pKey, data);
+ }
+ return status;
+ }
+
+ public OperationStatus getSearchKeyRange(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status = OperationStatus.NOTFOUND;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetSearchKeyRange(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ Cursor oldCursor = beginOperation();
+ try {
+ shareData(key, privKey);
+ status = doGetSearchKeyRange(lockMode);
+ if (status == OperationStatus.SUCCESS &&
+ !range.check(privKey)) {
+ status = OperationStatus.NOTFOUND;
+ }
+ } finally {
+ endOperation(oldCursor, status, key, pKey, data);
+ }
+ return status;
+ }
+
+ public OperationStatus getSearchBothRange(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status = OperationStatus.NOTFOUND;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetSearchBothRange(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ Cursor oldCursor = beginOperation();
+ try {
+ shareData(key, privKey);
+ if (secCursor != null) {
+ shareData(pKey, privPKey);
+ } else {
+ shareData(data, privData);
+ }
+ status = doGetSearchBothRange(lockMode);
+ if (status == OperationStatus.SUCCESS &&
+ !range.check(privKey)) {
+ status = OperationStatus.NOTFOUND;
+ }
+ } finally {
+ endOperation(oldCursor, status, key, pKey, data);
+ }
+ return status;
+ }
+
+ public OperationStatus getSearchRecordNumber(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ OperationStatus status;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetSearchRecordNumber(lockMode);
+ endOperation(null, status, null, null, null);
+ return status;
+ }
+ if (!range.check(key)) {
+ status = OperationStatus.NOTFOUND;
+ } else {
+ shareData(key, privKey);
+ status = doGetSearchRecordNumber(lockMode);
+ endOperation(null, status, key, pKey, data);
+ }
+ return status;
+ }
+
+ public OperationStatus getNextDup(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ if (!initialized) {
+ throw new DatabaseException("Cursor not initialized");
+ }
+ OperationStatus status;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetNextDup(lockMode);
+ endOperation(null, status, null, null, null);
+ } else {
+ status = doGetNextDup(lockMode);
+ endOperation(null, status, key, pKey, data);
+ }
+ return status;
+ }
+
+ public OperationStatus getPrevDup(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ if (!initialized) {
+ throw new DatabaseException("Cursor not initialized");
+ }
+ OperationStatus status;
+ if (!range.hasBound()) {
+ setParams(key, pKey, data);
+ status = doGetPrevDup(lockMode);
+ endOperation(null, status, null, null, null);
+ } else {
+ status = doGetPrevDup(lockMode);
+ endOperation(null, status, key, pKey, data);
+ }
+ return status;
+ }
+
+ public OperationStatus getCurrent(DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ if (!initialized) {
+ throw new DatabaseException("Cursor not initialized");
+ }
+ if (secCursor != null && pKey != null) {
+ return secCursor.getCurrent(key, pKey, data, lockMode);
+ } else {
+ return cursor.getCurrent(key, data, lockMode);
+ }
+ }
+
+ /*
+ * Pass-thru methods.
+ */
+
+ public void close()
+ throws DatabaseException {
+
+ currentTxn.closeCursor(cursor);
+ }
+
+ public int count()
+ throws DatabaseException {
+
+ return cursor.count();
+ }
+
+ public OperationStatus delete()
+ throws DatabaseException {
+
+ return cursor.delete();
+ }
+
+ public OperationStatus put(DatabaseEntry key, DatabaseEntry data)
+ throws DatabaseException {
+
+ return cursor.put(key, data);
+ }
+
+ public OperationStatus putNoOverwrite(DatabaseEntry key,
+ DatabaseEntry data)
+ throws DatabaseException {
+
+ return cursor.putNoOverwrite(key, data);
+ }
+
+ public OperationStatus putNoDupData(DatabaseEntry key, DatabaseEntry data)
+ throws DatabaseException {
+
+ return cursor.putNoDupData(key, data);
+ }
+
+ public OperationStatus putCurrent(DatabaseEntry data)
+ throws DatabaseException {
+
+ return cursor.putCurrent(data);
+ }
+
+ public OperationStatus putAfter(DatabaseEntry data)
+ throws DatabaseException {
+
+ return DbCompat.putAfter(cursor, data);
+ }
+
+ public OperationStatus putBefore(DatabaseEntry data)
+ throws DatabaseException {
+
+ return DbCompat.putBefore(cursor, data);
+ }
+
+ private OperationStatus doGetFirst(LockMode lockMode)
+ throws DatabaseException {
+
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getFirst(privKey, privPKey, privData, lockMode);
+ } else {
+ return cursor.getFirst(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetLast(LockMode lockMode)
+ throws DatabaseException {
+
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getLast(privKey, privPKey, privData, lockMode);
+ } else {
+ return cursor.getLast(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetNext(LockMode lockMode)
+ throws DatabaseException {
+
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getNext(privKey, privPKey, privData, lockMode);
+ } else {
+ return cursor.getNext(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetNextDup(LockMode lockMode)
+ throws DatabaseException {
+
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getNextDup(privKey, privPKey, privData, lockMode);
+ } else {
+ return cursor.getNextDup(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetNextNoDup(LockMode lockMode)
+ throws DatabaseException {
+
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getNextNoDup(privKey, privPKey, privData,
+ lockMode);
+ } else {
+ return cursor.getNextNoDup(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetPrev(LockMode lockMode)
+ throws DatabaseException {
+
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getPrev(privKey, privPKey, privData, lockMode);
+ } else {
+ return cursor.getPrev(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetPrevDup(LockMode lockMode)
+ throws DatabaseException {
+
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getPrevDup(privKey, privPKey, privData, lockMode);
+ } else {
+ return cursor.getPrevDup(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetPrevNoDup(LockMode lockMode)
+ throws DatabaseException {
+
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getPrevNoDup(privKey, privPKey, privData,
+ lockMode);
+ } else {
+ return cursor.getPrevNoDup(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetSearchKey(LockMode lockMode)
+ throws DatabaseException {
+
+ if (isRecnoOrQueue && DbCompat.getRecordNumber(privKey) <= 0) {
+ return OperationStatus.NOTFOUND;
+ }
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getSearchKey(privKey, privPKey, privData,
+ lockMode);
+ } else {
+ return cursor.getSearchKey(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetSearchKeyRange(LockMode lockMode)
+ throws DatabaseException {
+
+ if (isRecnoOrQueue && DbCompat.getRecordNumber(privKey) <= 0) {
+ return OperationStatus.NOTFOUND;
+ }
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getSearchKeyRange(privKey, privPKey, privData,
+ lockMode);
+ } else {
+ return cursor.getSearchKeyRange(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetSearchBoth(LockMode lockMode)
+ throws DatabaseException {
+
+ if (isRecnoOrQueue && DbCompat.getRecordNumber(privKey) <= 0) {
+ return OperationStatus.NOTFOUND;
+ }
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getSearchBoth(privKey, privPKey, privData,
+ lockMode);
+ } else {
+ return cursor.getSearchBoth(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetSearchBothRange(LockMode lockMode)
+ throws DatabaseException {
+
+ if (isRecnoOrQueue && DbCompat.getRecordNumber(privKey) <= 0) {
+ return OperationStatus.NOTFOUND;
+ }
+ if (secCursor != null && privPKey != null) {
+ return secCursor.getSearchBothRange(privKey, privPKey, privData,
+ lockMode);
+ } else {
+ return cursor.getSearchBothRange(privKey, privData, lockMode);
+ }
+ }
+
+ private OperationStatus doGetSearchRecordNumber(LockMode lockMode)
+ throws DatabaseException {
+
+ if (DbCompat.getRecordNumber(privKey) <= 0) {
+ return OperationStatus.NOTFOUND;
+ }
+ if (secCursor != null && privPKey != null) {
+ return DbCompat.getSearchRecordNumber(secCursor, privKey, privPKey,
+ privData, lockMode);
+ } else {
+ return DbCompat.getSearchRecordNumber(cursor, privKey, privData,
+ lockMode);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredCollection.java b/db/java/src/com/sleepycat/collections/StoredCollection.java
new file mode 100644
index 000000000..90f1a2f11
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredCollection.java
@@ -0,0 +1,460 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredCollection.java,v 1.4 2004/08/02 18:52:05 mjc Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.JoinConfig;
+import com.sleepycat.db.OperationStatus;
+
+/**
+ * A abstract base class for all stored collections. This class, and its
+ * base class {@link StoredContainer}, provide implementations of most methods
+ * in the {@link Collection} interface. Other methods, such as {@link #add}
+ * and {@link #remove}, are provided by concrete classes that extend this
+ * class.
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * <p>In addition, this class provides the following methods for stored
+ * collections only. Note that the use of these methods is not compatible with
+ * the standard Java collections interface.</p>
+ * <ul>
+ * <li>{@link #iterator(boolean)}</li>
+ * <li>{@link #join}</li>
+ * <li>{@link #toList()}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public abstract class StoredCollection extends StoredContainer
+ implements Collection {
+
+ StoredCollection(DataView view) {
+
+ super(view);
+ }
+
+ final boolean add(Object key, Object value) {
+
+ DataCursor cursor = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ cursor = new DataCursor(view, true);
+ OperationStatus status =
+ cursor.putNoDupData(key, value, null, false);
+ closeCursor(cursor);
+ commitAutoCommit(doAutoCommit);
+ return (status == OperationStatus.SUCCESS);
+ } catch (Exception e) {
+ closeCursor(cursor);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Returns an iterator over the elements in this collection.
+ * The iterator will be read-only if the collection is read-only.
+ * This method conforms to the {@link Collection#iterator} interface.
+ *
+ * @return a {@link StoredIterator} for this collection.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ *
+ * @see #isWriteAllowed
+ */
+ public Iterator iterator() {
+
+ return iterator(isWriteAllowed());
+ }
+
+ /**
+ * Returns a read or read-write iterator over the elements in this
+ * collection.
+ * This method does not exist in the standard {@link Collection} interface.
+ *
+ * @param writeAllowed is true to open a read-write iterator or false to
+ * open a read-only iterator. If the collection is read-only the iterator
+ * will always be read-only.
+ *
+ * @return a {@link StoredIterator} for this collection.
+ *
+ * @throws IllegalStateException if writeAllowed is true but the collection
+ * is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ *
+ * @see #isWriteAllowed
+ */
+ public StoredIterator iterator(boolean writeAllowed) {
+
+ try {
+ return new StoredIterator(this, writeAllowed && isWriteAllowed(),
+ null);
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Returns an array of all the elements in this collection.
+ * This method conforms to the {@link Collection#toArray()} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public Object[] toArray() {
+
+ ArrayList list = new ArrayList();
+ Iterator i = iterator();
+ try {
+ while (i.hasNext()) {
+ list.add(i.next());
+ }
+ } finally {
+ StoredIterator.close(i);
+ }
+ return list.toArray();
+ }
+
+ /**
+ * Returns an array of all the elements in this collection whose runtime
+ * type is that of the specified array.
+ * This method conforms to the {@link Collection#toArray(Object[])}
+ * interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public Object[] toArray(Object[] a) {
+
+ int j = 0;
+ Iterator i = iterator();
+ try {
+ while (j < a.length && i.hasNext()) {
+ a[j++] = i.next();
+ }
+ if (j < a.length) {
+ a[j] = null;
+ } else if (i.hasNext()) {
+ ArrayList list = new ArrayList(Arrays.asList(a));
+ while (i.hasNext()) {
+ list.add(i.next());
+ }
+ a = list.toArray(a);
+ }
+ } finally {
+ StoredIterator.close(i);
+ }
+ return a;
+ }
+
+ /**
+ * Returns true if this collection contains all of the elements in the
+ * specified collection.
+ * This method conforms to the {@link Collection#containsAll} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean containsAll(Collection coll) {
+ Iterator i = coll.iterator();
+ try {
+ while (i.hasNext()) {
+ if (!contains(i.next())) {
+ return false;
+ }
+ }
+ } finally {
+ StoredIterator.close(i);
+ }
+ return true;
+ }
+
+ /**
+ * Adds all of the elements in the specified collection to this collection
+ * (optional operation).
+ * This method calls the {@link #add(Object)} method of the concrete
+ * collection class, which may or may not be supported.
+ * This method conforms to the {@link Collection#addAll} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only, or
+ * if the collection is indexed, or if the add method is not supported by
+ * the concrete collection.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean addAll(Collection coll) {
+ Iterator i = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ i = coll.iterator();
+ boolean changed = false;
+ while (i.hasNext()) {
+ if (add(i.next())) {
+ changed = true;
+ }
+ }
+ StoredIterator.close(i);
+ commitAutoCommit(doAutoCommit);
+ return changed;
+ } catch (Exception e) {
+ StoredIterator.close(i);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Removes all this collection's elements that are also contained in the
+ * specified collection (optional operation).
+ * This method conforms to the {@link Collection#removeAll} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean removeAll(Collection coll) {
+
+ return removeAll(coll, true);
+ }
+
+ /**
+ * Retains only the elements in this collection that are contained in the
+ * specified collection (optional operation).
+ * This method conforms to the {@link Collection#removeAll} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean retainAll(Collection coll) {
+
+ return removeAll(coll, false);
+ }
+
+ private boolean removeAll(Collection coll, boolean ifExistsInColl) {
+ Iterator i = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ boolean changed = false;
+ i = iterator();
+ while (i.hasNext()) {
+ if (ifExistsInColl == coll.contains(i.next())) {
+ i.remove();
+ changed = true;
+ }
+ }
+ StoredIterator.close(i);
+ commitAutoCommit(doAutoCommit);
+ return changed;
+ } catch (Exception e) {
+ StoredIterator.close(i);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Compares the specified object with this collection for equality.
+ * A value comparison is performed by this method and the stored values
+ * are compared rather than calling the equals() method of each element.
+ * This method conforms to the {@link Collection#equals} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean equals(Object other) {
+
+ if (other instanceof Collection) {
+ Collection otherColl = StoredCollection.copyCollection(other);
+ Iterator i = iterator();
+ try {
+ while (i.hasNext()) {
+ if (!otherColl.remove(i.next())) {
+ return false;
+ }
+ }
+ return otherColl.isEmpty();
+ } finally {
+ StoredIterator.close(i);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /*
+ * Add this in to keep FindBugs from whining at us about implementing
+ * equals(), but not hashCode().
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ /**
+ * Returns a copy of this collection as an ArrayList. This is the same as
+ * {@link #toArray()} but returns a collection instead of an array.
+ *
+ * @return an {@link ArrayList} containing a copy of all elements in this
+ * collection.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public List toList() {
+
+ ArrayList list = new ArrayList();
+ Iterator i = iterator();
+ try {
+ while (i.hasNext()) list.add(i.next());
+ return list;
+ } finally {
+ StoredIterator.close(i);
+ }
+ }
+
+ /**
+ * Converts the collection to a string representation for debugging.
+ * WARNING: All elements will be converted to strings and returned and
+ * therefore the returned string may be very large.
+ *
+ * @return the string representation.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("[");
+ Iterator i = iterator();
+ try {
+ while (i.hasNext()) {
+ if (buf.length() > 1) buf.append(',');
+ buf.append(i.next().toString());
+ }
+ buf.append(']');
+ return buf.toString();
+ } finally {
+ StoredIterator.close(i);
+ }
+ }
+
+ /**
+ * Returns an iterator representing an equality join of the indices and
+ * index key values specified.
+ * This method does not exist in the standard {@link Collection} interface.
+ *
+ * <p>The returned iterator supports only the two methods: hasNext() and
+ * next(). All other methods will throw UnsupportedOperationException.</p>
+ *
+ * @param indices is an array of indices with elements corresponding to
+ * those in the indexKeys array.
+ *
+ * @param indexKeys is an array of index key values identifying the
+ * elements to be selected.
+ *
+ * @param joinConfig is the join configuration, or null to use the
+ * default configuration.
+ *
+ * @return an iterator over the elements in this collection that match
+ * all specified index key values.
+ *
+ * @throws IllegalArgumentException if this collection is indexed or if a
+ * given index does not have the same store as this collection.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public StoredIterator join(StoredContainer[] indices, Object[] indexKeys,
+ JoinConfig joinConfig) {
+
+ try {
+ DataView[] indexViews = new DataView[indices.length];
+ for (int i = 0; i < indices.length; i += 1) {
+ indexViews[i] = indices[i].view;
+ }
+ DataCursor cursor = view.join(indexViews, indexKeys, joinConfig);
+ return new StoredIterator(this, false, cursor);
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ final Object getFirstOrLast(boolean doGetFirst) {
+
+ DataCursor cursor = null;
+ try {
+ cursor = new DataCursor(view, false);
+ OperationStatus status;
+ if (doGetFirst) {
+ status = cursor.getFirst(false);
+ } else {
+ status = cursor.getLast(false);
+ }
+ return (status == OperationStatus.SUCCESS) ?
+ makeIteratorData(null, cursor) : null;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ } finally {
+ closeCursor(cursor);
+ }
+ }
+
+ abstract Object makeIteratorData(StoredIterator iterator,
+ DataCursor cursor)
+ throws DatabaseException;
+
+ abstract boolean hasValues();
+
+ boolean iterateDuplicates() {
+
+ return true;
+ }
+
+ void checkIterAddAllowed()
+ throws UnsupportedOperationException {
+
+ if (!areDuplicatesAllowed()) {
+ throw new UnsupportedOperationException("duplicates required");
+ }
+ }
+
+ int getIndexOffset() {
+
+ return 0;
+ }
+
+ private static Collection copyCollection(Object other) {
+
+ if (other instanceof StoredCollection) {
+ return ((StoredCollection) other).toList();
+ } else {
+ return new ArrayList((Collection) other);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredCollections.java b/db/java/src/com/sleepycat/collections/StoredCollections.java
new file mode 100644
index 000000000..c9a4ab9fc
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredCollections.java
@@ -0,0 +1,156 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredCollections.java,v 1.1 2004/04/09 16:34:09 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/**
+ * This class consists exclusively of static methods that operate on or return
+ * stored collections. It contains methods for changing certain properties of a
+ * collection. Because collection properties are immutable, these methods
+ * always return a new collection reference. This allows stored collections to
+ * be used safely by multiple threads. Note that creating the new collection
+ * reference is not expensive and creates only two new objects.
+ *
+ * <p>When a collection is created with a particular property, all collections
+ * and iterators derived from that collection will inherit the property. For
+ * example, if a dirty-read Map is created then calls to subMap(), values(),
+ * entrySet(), and keySet() will create dirty-read collections also.</p>
+ *
+ * <p><b>Dirty-Read</b> Methods names beginning with dirtyRead create a new
+ * dirty-read container from a given stored container. When dirty-read is
+ * enabled, data will be read that has been modified by another transaction but
+ * not committed. Using dirty-read can improve concurrency since reading will
+ * not wait for other transactions to complete. For a non-transactional
+ * container (when {@link StoredContainer#isTransactional} returns false),
+ * dirty-read has no effect. If {@link StoredContainer#isDirtyReadAllowed}
+ * returns false, dirty-read also has no effect. If dirty-ready is enabled
+ * (and allowed) for a container, {@link StoredContainer#isDirtyRead} will
+ * return true. Dirty-read is disabled by default for a container.</p>
+ */
+public class StoredCollections {
+
+ private StoredCollections() {}
+
+ /**
+ * Creates a dirty-read collection from a given stored collection.
+ *
+ * @param storedCollection the base collection.
+ *
+ * @return the dirty-read collection.
+ *
+ * @throws ClassCastException if the given container is not a
+ * StoredContainer.
+ */
+ public static Collection dirtyReadCollection(Collection storedCollection) {
+
+ return (Collection)
+ ((StoredContainer) storedCollection).dirtyReadClone();
+ }
+
+ /**
+ * Creates a dirty-read list from a given stored list.
+ *
+ * @param storedList the base list.
+ *
+ * @return the dirty-read list.
+ *
+ * @throws ClassCastException if the given container is not a
+ * StoredContainer.
+ */
+ public static List dirtyReadList(List storedList) {
+
+ return (List) ((StoredContainer) storedList).dirtyReadClone();
+ }
+
+ /**
+ * Creates a dirty-read map from a given stored map.
+ *
+ * @param storedMap the base map.
+ *
+ * @return the dirty-read map.
+ *
+ * @throws ClassCastException if the given container is not a
+ * StoredContainer.
+ */
+ public static Map dirtyReadMap(Map storedMap) {
+
+ return (Map) ((StoredContainer) storedMap).dirtyReadClone();
+ }
+
+ /**
+ * Creates a dirty-read set from a given stored set.
+ *
+ * @param storedSet the base set.
+ *
+ * @return the dirty-read set.
+ *
+ * @throws ClassCastException if the given container is not a
+ * StoredContainer.
+ */
+ public static Set dirtyReadSet(Set storedSet) {
+
+ return (Set) ((StoredContainer) storedSet).dirtyReadClone();
+ }
+
+ /**
+ * Creates a dirty-read sorted map from a given stored sorted map.
+ *
+ * @param storedSortedMap the base map.
+ *
+ * @return the dirty-read map.
+ *
+ * @throws ClassCastException if the given container is not a
+ * StoredContainer.
+ */
+ public static SortedMap dirtyReadSortedMap(SortedMap storedSortedMap) {
+
+ return (SortedMap)
+ ((StoredContainer) storedSortedMap).dirtyReadClone();
+ }
+
+ /**
+ * Creates a dirty-read sorted set from a given stored sorted set.
+ *
+ * @param storedSortedSet the base set.
+ *
+ * @return the dirty-read set.
+ *
+ * @throws ClassCastException if the given container is not a
+ * StoredContainer.
+ */
+ public static SortedSet dirtyReadSortedSet(SortedSet storedSortedSet) {
+
+ return (SortedSet)
+ ((StoredContainer) storedSortedSet).dirtyReadClone();
+ }
+
+ /**
+ * Clones a stored iterator preserving its current position.
+ *
+ * @param storedIterator an iterator to clone.
+ *
+ * @return a new {@link StoredIterator} having the same position as the
+ * given iterator.
+ *
+ * @throws ClassCastException if the given iterator is not a
+ * StoredIterator.
+ */
+ public static Iterator iterator(Iterator storedIterator) {
+
+ return (Iterator) ((StoredIterator) storedIterator).clone();
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredContainer.java b/db/java/src/com/sleepycat/collections/StoredContainer.java
new file mode 100644
index 000000000..f503a5dc8
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredContainer.java
@@ -0,0 +1,415 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredContainer.java,v 1.2 2004/06/02 20:59:38 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * A abstract base class for all stored collections and maps. This class
+ * provides implementations of methods that are common to the {@link
+ * java.util.Collection} and the {@link java.util.Map} interfaces, namely
+ * {@link #clear}, {@link #isEmpty} and {@link #size}.
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * <p>In addition, this class provides the following methods for stored
+ * collections only. Note that the use of these methods is not compatible with
+ * the standard Java collections interface.</p>
+ * <ul>
+ * <li>{@link #isWriteAllowed()}</li>
+ * <li>{@link #isSecondary()}</li>
+ * <li>{@link #isOrdered()}</li>
+ * <li>{@link #areDuplicatesAllowed()}</li>
+ * <li>{@link #areDuplicatesOrdered()}</li>
+ * <li>{@link #areKeysRenumbered()}</li>
+ * <li>{@link #isDirtyReadAllowed()}</li>
+ * <li>{@link #isDirtyRead()}</li>
+ * <li>{@link #isTransactional()}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public abstract class StoredContainer implements Cloneable {
+
+ DataView view;
+
+ StoredContainer(DataView view) {
+
+ this.view = view;
+ }
+
+ /**
+ * Returns true if this is a read-write container or false if this is a
+ * read-only container.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether write is allowed.
+ */
+ public final boolean isWriteAllowed() {
+
+ return view.writeAllowed;
+ }
+
+ /**
+ * Returns whether dirty-read is allowed for this container.
+ * For the JE product, dirty-read is always allowed; for the DB product,
+ * dirty-read is allowed if it was configured for the underlying database
+ * for this container.
+ * Even when dirty-read is allowed it must specifically be enabled by
+ * calling one of the {@link StoredCollections} methods.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether dirty-read is allowed.
+ */
+ public final boolean isDirtyReadAllowed() {
+
+ return view.dirtyReadAllowed;
+ }
+
+ /**
+ * Returns whether dirty-read is enabled for this container.
+ * If dirty-read is enabled, data will be read that is modified but not
+ * committed.
+ * Dirty-read is disabled by default.
+ * This method always returns false if {@link #isDirtyReadAllowed} returns
+ * false.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether dirty-read is enabled.
+ */
+ public final boolean isDirtyRead() {
+
+ return view.dirtyReadEnabled;
+ }
+
+ /**
+ * Returns whether the databases underlying this container are
+ * transactional.
+ * Even in a transactional environment, a database will be transactional
+ * only if it was opened within a transaction or if the auto-commit option
+ * was specified when it was opened.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether the database is transactional.
+ */
+ public final boolean isTransactional() {
+
+ return view.transactional;
+ }
+
+ /**
+ * Clones and enables dirty-read in the clone.
+ */
+ final StoredContainer dirtyReadClone() {
+
+ if (!isDirtyReadAllowed())
+ return this;
+ try {
+ StoredContainer cont = (StoredContainer) clone();
+ cont.view = cont.view.dirtyReadView(true);
+ return cont;
+ } catch (CloneNotSupportedException willNeverOccur) { return null; }
+ }
+
+ /**
+ * Returns whether duplicate keys are allowed in this container.
+ * Duplicates are optionally allowed for HASH and BTREE databases.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether duplicates are allowed.
+ */
+ public final boolean areDuplicatesAllowed() {
+
+ return view.dupsAllowed;
+ }
+
+ /**
+ * Returns whether duplicate keys are allowed and sorted by element value.
+ * Duplicates are optionally sorted for HASH and BTREE databases.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether duplicates are ordered.
+ */
+ public final boolean areDuplicatesOrdered() {
+
+ return view.dupsOrdered;
+ }
+
+ /**
+ * Returns whether keys are renumbered when insertions and deletions occur.
+ * Keys are optionally renumbered for RECNO databases.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether keys are renumbered.
+ */
+ public final boolean areKeysRenumbered() {
+
+ return view.keysRenumbered;
+ }
+
+ /**
+ * Returns whether keys are ordered in this container.
+ * Keys are ordered for BTREE, RECNO and QUEUE database.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether keys are ordered.
+ */
+ public final boolean isOrdered() {
+
+ return view.ordered;
+ }
+
+ /**
+ * Returns whether this container is a view on a secondary database rather
+ * than directly on a primary database.
+ * This method does not exist in the standard {@link java.util.Map} or
+ * {@link java.util.Collection} interfaces.
+ *
+ * @return whether the view is for a secondary database.
+ */
+ public final boolean isSecondary() {
+
+ return view.isSecondary();
+ }
+
+ /**
+ * Always throws UnsupportedOperationException. The size of a database
+ * cannot be obtained reliably or inexpensively.
+ * This method therefore violates the {@link java.util.Collection#size} and
+ * {@link java.util.Map#size} interfaces.
+ *
+ * @return always throws an exception.
+ *
+ * @throws UnsupportedOperationException unconditionally.
+ */
+ public int size() {
+
+ throw new UnsupportedOperationException(
+ "collection size not available");
+ }
+
+ /**
+ * Returns true if this map or collection contains no mappings or elements.
+ * This method conforms to the {@link java.util.Collection#isEmpty} and
+ * {@link java.util.Map#isEmpty} interfaces.
+ *
+ * @return whether the container is empty.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
+ */
+ public boolean isEmpty() {
+
+ try {
+ return view.isEmpty();
+ } catch (Exception e) {
+ throw convertException(e);
+ }
+ }
+
+ /**
+ * Removes all mappings or elements from this map or collection (optional
+ * operation).
+ * This method conforms to the {@link java.util.Collection#clear} and
+ * {@link java.util.Map#clear} interfaces.
+ *
+ * @throws UnsupportedOperationException if the container is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
+ */
+ public void clear() {
+
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ view.clear();
+ commitAutoCommit(doAutoCommit);
+ } catch (Exception e) {
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ Object get(Object key) {
+
+ DataCursor cursor = null;
+ try {
+ cursor = new DataCursor(view, false);
+ if (OperationStatus.SUCCESS ==
+ cursor.getSearchKey(key, null, false)) {
+ return cursor.getCurrentValue();
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ } finally {
+ closeCursor(cursor);
+ }
+ }
+
+ Object put(final Object key, final Object value) {
+
+ DataCursor cursor = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ cursor = new DataCursor(view, true);
+ Object[] oldValue = new Object[1];
+ cursor.put(key, value, oldValue, false);
+ closeCursor(cursor);
+ commitAutoCommit(doAutoCommit);
+ return oldValue[0];
+ } catch (Exception e) {
+ closeCursor(cursor);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ final boolean removeKey(final Object key, final Object[] oldVal) {
+
+ DataCursor cursor = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ cursor = new DataCursor(view, true);
+ boolean found = false;
+ OperationStatus status = cursor.getSearchKey(key, null, true);
+ while (status == OperationStatus.SUCCESS) {
+ cursor.delete();
+ found = true;
+ if (oldVal != null && oldVal[0] == null) {
+ oldVal[0] = cursor.getCurrentValue();
+ }
+ status = cursor.getNextDup(true);
+ }
+ closeCursor(cursor);
+ commitAutoCommit(doAutoCommit);
+ return found;
+ } catch (Exception e) {
+ closeCursor(cursor);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ boolean containsKey(Object key) {
+
+ DataCursor cursor = null;
+ try {
+ cursor = new DataCursor(view, false);
+ return OperationStatus.SUCCESS ==
+ cursor.getSearchKey(key, null, false);
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ } finally {
+ closeCursor(cursor);
+ }
+ }
+
+ final boolean removeValue(Object value) {
+
+ DataCursor cursor = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ cursor = new DataCursor(view, true);
+ OperationStatus status = cursor.find(value, true);
+ if (status == OperationStatus.SUCCESS) {
+ cursor.delete();
+ }
+ closeCursor(cursor);
+ commitAutoCommit(doAutoCommit);
+ return (status == OperationStatus.SUCCESS);
+ } catch (Exception e) {
+ closeCursor(cursor);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ boolean containsValue(Object value) {
+
+ DataCursor cursor = null;
+ try {
+ cursor = new DataCursor(view, false);
+ OperationStatus status = cursor.find(value, true);
+ return (status == OperationStatus.SUCCESS);
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ } finally {
+ closeCursor(cursor);
+ }
+ }
+
+ final void closeCursor(DataCursor cursor) {
+
+ if (cursor != null) {
+ try {
+ cursor.close();
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+ }
+
+ final boolean beginAutoCommit() {
+
+ if (view.transactional) {
+ try {
+ CurrentTransaction currentTxn = view.getCurrentTxn();
+ if (currentTxn.getTransaction() == null) {
+ currentTxn.beginTransaction(null);
+ return true;
+ }
+ } catch (DatabaseException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+ return false;
+ }
+
+ final void commitAutoCommit(boolean doAutoCommit)
+ throws DatabaseException {
+
+ if (doAutoCommit) view.getCurrentTxn().commitTransaction();
+ }
+
+ final RuntimeException handleException(Exception e, boolean doAutoCommit) {
+
+ if (doAutoCommit) {
+ try {
+ view.getCurrentTxn().abortTransaction();
+ } catch (DatabaseException ignored) {
+ }
+ }
+ return StoredContainer.convertException(e);
+ }
+
+ static RuntimeException convertException(Exception e) {
+
+ if (e instanceof RuntimeException) {
+ return (RuntimeException) e;
+ } else {
+ return new RuntimeExceptionWrapper(e);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredEntrySet.java b/db/java/src/com/sleepycat/collections/StoredEntrySet.java
new file mode 100644
index 000000000..fbc5f85d2
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredEntrySet.java
@@ -0,0 +1,176 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredEntrySet.java,v 1.3 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.OperationStatus;
+
+/**
+ * The Set returned by Map.entrySet(). This class may not be instantiated
+ * directly. Contrary to what is stated by {@link Map#entrySet} this class
+ * does support the {@link #add} and {@link #addAll} methods.
+ *
+ * <p>The {@link java.util.Map.Entry#setValue} method of the Map.Entry objects
+ * that are returned by this class and its iterators behaves just as the {@link
+ * StoredIterator#set} method does.</p>
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredEntrySet extends StoredCollection implements Set {
+
+ StoredEntrySet(DataView mapView) {
+
+ super(mapView);
+ }
+
+ /**
+ * Adds the specified element to this set if it is not already present
+ * (optional operation).
+ * This method conforms to the {@link Set#add} interface.
+ *
+ * @param mapEntry must be a {@link java.util.Map.Entry} instance.
+ *
+ * @return true if the key-value pair was added to the set (and was not
+ * previously present).
+ *
+ * @throws UnsupportedOperationException if the collection is read-only.
+ *
+ * @throws ClassCastException if the mapEntry is not a {@link
+ * java.util.Map.Entry} instance.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
+ */
+ public boolean add(Object mapEntry) {
+
+ Map.Entry entry = (Map.Entry) mapEntry; // allow ClassCastException
+ return add(entry.getKey(), entry.getValue());
+ }
+
+ /**
+ * Removes the specified element from this set if it is present (optional
+ * operation).
+ * This method conforms to the {@link Set#remove} interface.
+ *
+ * @param mapEntry is a {@link java.util.Map.Entry} instance to be removed.
+ *
+ * @return true if the key-value pair was removed from the set, or false if
+ * the mapEntry is not a {@link java.util.Map.Entry} instance or is not
+ * present in the set.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
+ */
+ public boolean remove(Object mapEntry) {
+
+ if (!(mapEntry instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry entry = (Map.Entry) mapEntry;
+ DataCursor cursor = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ cursor = new DataCursor(view, true);
+ OperationStatus status =
+ cursor.getSearchBoth(entry.getKey(), entry.getValue(), true);
+ if (status == OperationStatus.SUCCESS) {
+ cursor.delete();
+ }
+ closeCursor(cursor);
+ commitAutoCommit(doAutoCommit);
+ return (status == OperationStatus.SUCCESS);
+ } catch (Exception e) {
+ closeCursor(cursor);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Returns true if this set contains the specified element.
+ * This method conforms to the {@link Set#contains} interface.
+ *
+ * @param mapEntry is a {@link java.util.Map.Entry} instance to be checked.
+ *
+ * @return true if the key-value pair is present in the set, or false if
+ * the mapEntry is not a {@link java.util.Map.Entry} instance or is not
+ * present in the set.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
+ */
+ public boolean contains(Object mapEntry) {
+
+ if (!(mapEntry instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry entry = (Map.Entry) mapEntry;
+ DataCursor cursor = null;
+ try {
+ cursor = new DataCursor(view, false);
+ OperationStatus status =
+ cursor.getSearchBoth(entry.getKey(), entry.getValue(), false);
+ return (status == OperationStatus.SUCCESS);
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ } finally {
+ closeCursor(cursor);
+ }
+ }
+
+ // javadoc is inherited
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("[");
+ Iterator i = iterator();
+ try {
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry) i.next();
+ if (buf.length() > 1) buf.append(',');
+ Object key = entry.getKey();
+ Object val = entry.getValue();
+ if (key != null) buf.append(key.toString());
+ buf.append('=');
+ if (val != null) buf.append(val.toString());
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+ finally {
+ StoredIterator.close(i);
+ }
+ }
+
+ Object makeIteratorData(StoredIterator iterator, DataCursor cursor)
+ throws DatabaseException {
+
+ return new StoredMapEntry(cursor.getCurrentKey(),
+ cursor.getCurrentValue(),
+ this, iterator);
+ }
+
+ boolean hasValues() {
+
+ return true;
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredIterator.java b/db/java/src/com/sleepycat/collections/StoredIterator.java
new file mode 100644
index 000000000..cfc7c67a4
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredIterator.java
@@ -0,0 +1,600 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredIterator.java,v 1.5 2004/09/22 18:01:03 bostic Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.OperationStatus;
+
+/**
+ * The Iterator returned by all stored collections.
+ *
+ * <p>While in general this class conforms to the {@link Iterator} interface,
+ * it is important to note that all iterators for stored collections must be
+ * explicitly closed with {@link #close()}. The static method {@link
+ * #close(java.util.Iterator)} allows calling close for all iterators without
+ * harm to iterators that are not from stored collections, and also avoids
+ * casting. If a stored iterator is not closed, unpredictable behavior
+ * including process death may result.</p>
+ *
+ * <p>This class implements the {@link Iterator} interface for all stored
+ * iterators. It also implements {@link ListIterator} because some list
+ * iterator methods apply to all stored iterators, for example, {@link
+ * #previous} and {@link #hasPrevious}. Other list iterator methods are always
+ * supported for lists, but for other types of collections are only supported
+ * under certain conditions. See {@link #nextIndex}, {@link #previousIndex},
+ * {@link #add} and {@link #set} for details.</p>
+ *
+ * <p>In addition, this class provides the following methods for stored
+ * collection iterators only. Note that the use of these methods is not
+ * compatible with the standard Java collections interface.</p>
+ * <ul>
+ * <li>{@link #close()}</li>
+ * <li>{@link #close(Iterator)}</li>
+ * <li>{@link #getCollection}</li>
+ * <li>{@link #setReadModifyWrite}</li>
+ * <li>{@link #isReadModifyWrite}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredIterator implements ListIterator, Cloneable {
+
+ /**
+ * Closes the given iterator using {@link #close()} if it is a {@link
+ * StoredIterator}. If the given iterator is not a {@link StoredIterator},
+ * this method does nothing.
+ *
+ * @param i is the iterator to close.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
+ */
+ public static void close(Iterator i) {
+
+ if (i instanceof StoredIterator) {
+ ((StoredIterator) i).close();
+ }
+ }
+
+ private static final int MOVE_NEXT = 1;
+ private static final int MOVE_PREV = 2;
+ private static final int MOVE_FIRST = 3;
+
+ private boolean lockForWrite;
+ private StoredCollection coll;
+ private DataCursor cursor;
+ private int toNext;
+ private int toPrevious;
+ private int toCurrent;
+ private boolean writeAllowed;
+ private boolean setAndRemoveAllowed;
+ private Object currentData;
+ private final boolean recNumAccess;
+
+ StoredIterator(StoredCollection coll, boolean writeAllowed,
+ DataCursor joinCursor) {
+ try {
+ this.coll = coll;
+ this.writeAllowed = writeAllowed;
+ if (joinCursor == null)
+ this.cursor = new DataCursor(coll.view, writeAllowed);
+ else
+ this.cursor = joinCursor;
+ this.recNumAccess = cursor.hasRecNumAccess();
+ reset();
+ } catch (Exception e) {
+ try {
+ /* Ensure that the cursor is closed. [#10516] */
+ close();
+ } catch (Exception ignored) {}
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Clones this iterator preserving its current position.
+ *
+ * @return a new {@link StoredIterator} having the same position as this
+ * iterator.
+ */
+ protected Object clone() {
+
+ try {
+ StoredIterator o = (StoredIterator) super.clone();
+ o.cursor = cursor.cloneCursor();
+ return o;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Returns whether write-locks will be obtained when reading with this
+ * cursor.
+ * Obtaining write-locks can prevent deadlocks when reading and then
+ * modifying data.
+ *
+ * @return the write-lock setting.
+ */
+ public final boolean isReadModifyWrite() {
+
+ return lockForWrite;
+ }
+
+ /**
+ * Changes whether write-locks will be obtained when reading with this
+ * cursor.
+ * Obtaining write-locks can prevent deadlocks when reading and then
+ * modifying data.
+ *
+ * @param lockForWrite the write-lock setting.
+ */
+ public void setReadModifyWrite(boolean lockForWrite) {
+
+ this.lockForWrite = lockForWrite;
+ }
+
+ // --- begin Iterator/ListIterator methods ---
+
+ /**
+ * Returns true if this iterator has more elements when traversing in the
+ * forward direction. False is returned if the iterator has been closed.
+ * This method conforms to the {@link Iterator#hasNext} interface.
+ *
+ * @return whether {@link #next()} will succeed.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
+ */
+ public boolean hasNext() {
+
+ if (cursor == null) {
+ return false;
+ }
+ try {
+ if (toNext != 0) {
+ OperationStatus status = move(toNext);
+ if (status == OperationStatus.SUCCESS) {
+ toNext = 0;
+ toPrevious = MOVE_PREV;
+ toCurrent = MOVE_PREV;
+ }
+ }
+ return (toNext == 0);
+ }
+ catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Returns true if this iterator has more elements when traversing in the
+ * reverse direction. It returns false if the iterator has been closed.
+ * This method conforms to the {@link ListIterator#hasPrevious} interface.
+ *
+ * @return whether {@link #previous()} will succeed.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
+ */
+ public boolean hasPrevious() {
+
+ if (cursor == null) {
+ return false;
+ }
+ try {
+ if (toPrevious != 0) {
+ OperationStatus status = move(toPrevious);
+ if (status == OperationStatus.SUCCESS) {
+ toPrevious = 0;
+ toNext = MOVE_NEXT;
+ toCurrent = MOVE_NEXT;
+ }
+ }
+ return (toPrevious == 0);
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Returns the next element in the iteration.
+ * This method conforms to the {@link Iterator#next} interface.
+ *
+ * @return the next element.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public Object next() {
+
+ try {
+ if (toNext != 0) {
+ OperationStatus status = move(toNext);
+ if (status == OperationStatus.SUCCESS) {
+ toNext = 0;
+ }
+ }
+ if (toNext == 0) {
+ currentData = coll.makeIteratorData(this, cursor);
+ toNext = MOVE_NEXT;
+ toPrevious = 0;
+ toCurrent = 0;
+ setAndRemoveAllowed = true;
+ return currentData;
+ }
+ // else throw NoSuchElementException below
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * Returns the next element in the iteration.
+ * This method conforms to the {@link ListIterator#previous} interface.
+ *
+ * @return the previous element.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public Object previous() {
+
+ try {
+ if (toPrevious != 0) {
+ OperationStatus status = move(toPrevious);
+ if (status == OperationStatus.SUCCESS) {
+ toPrevious = 0;
+ }
+ }
+ if (toPrevious == 0) {
+ currentData = coll.makeIteratorData(this, cursor);
+ toPrevious = MOVE_PREV;
+ toNext = 0;
+ toCurrent = 0;
+ setAndRemoveAllowed = true;
+ return currentData;
+ }
+ // else throw NoSuchElementException below
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * Returns the index of the element that would be returned by a subsequent
+ * call to next.
+ * This method conforms to the {@link ListIterator#nextIndex} interface
+ * except that it returns Integer.MAX_VALUE for stored lists when
+ * positioned at the end of the list, rather than returning the list size
+ * as specified by the ListIterator interface. This is because the database
+ * size is not available.
+ *
+ * @return the next index.
+ *
+ * @throws UnsupportedOperationException if this iterator's collection does
+ * not use record number keys.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public int nextIndex() {
+
+ if (!recNumAccess) {
+ throw new UnsupportedOperationException(
+ "Record number access not supported");
+ }
+ try {
+ return hasNext() ? (cursor.getCurrentRecordNumber() -
+ coll.getIndexOffset())
+ : Integer.MAX_VALUE;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Returns the index of the element that would be returned by a subsequent
+ * call to previous.
+ * This method conforms to the {@link ListIterator#previousIndex}
+ * interface.
+ *
+ * @return the previous index.
+ *
+ * @throws UnsupportedOperationException if this iterator's collection does
+ * not use record number keys.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public int previousIndex() {
+
+ if (!recNumAccess) {
+ throw new UnsupportedOperationException(
+ "Record number access not supported");
+ }
+ try {
+ return hasPrevious() ? (cursor.getCurrentRecordNumber() -
+ coll.getIndexOffset())
+ : -1;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Replaces the last element returned by next or previous with the
+ * specified element (optional operation).
+ * This method conforms to the {@link ListIterator#set} interface.
+ *
+ * @param value the new value.
+ *
+ * @throws UnsupportedOperationException if the collection is a {@link
+ * StoredKeySet} (the set returned by {@link java.util.Map#keySet}), or if
+ * duplicates are sorted since this would change the iterator position, or
+ * if the collection is indexed, or if the collection is read-only.
+ *
+ * @throws IllegalArgumentException if an entity value binding is used and
+ * the primary key of the value given is different than the existing stored
+ * primary key.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public void set(Object value) {
+
+ if (!coll.hasValues()) throw new UnsupportedOperationException();
+ if (!setAndRemoveAllowed) throw new IllegalStateException();
+ try {
+ moveToCurrent();
+ cursor.putCurrent(value);
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Removes the last element that was returned by next or previous (optional
+ * operation).
+ * This method conforms to the {@link ListIterator#remove} interface except
+ * that when the collection is a list and the RECNO-RENUMBER access method
+ * is not used, list indices will not be renumbered.
+ *
+ * @throws UnsupportedOperationException if the collection is a sublist, or
+ * if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public void remove() {
+
+ if (!setAndRemoveAllowed) throw new IllegalStateException();
+ try {
+ moveToCurrent();
+ cursor.delete();
+ setAndRemoveAllowed = false;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Inserts the specified element into the list or inserts a duplicate into
+ * other types of collections (optional operation).
+ * This method conforms to the {@link ListIterator#add} interface when
+ * the collection is a list and the RECNO-RENUMBER access method is used.
+ * Otherwise, this method may only be called when duplicates are allowed.
+ * If duplicates are unsorted, the new value will be inserted in the same
+ * manner as list elements.
+ * If duplicates are sorted, the new value will be inserted in sort order.
+ *
+ * @param value the new value.
+ *
+ * @throws UnsupportedOperationException if the collection is a sublist, or
+ * if the collection is indexed, or if the collection is read-only, or if
+ * the collection is a list and the RECNO-RENUMBER access method was not
+ * used, or if the collection is not a list and duplicates are not allowed.
+ *
+ * @throws IllegalStateException if the collection is empty and is not a
+ * list with RECNO-RENUMBER access.
+ *
+ * @throws IllegalArgumentException if a duplicate value is being added
+ * that already exists and duplicates are sorted.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public void add(Object value) {
+
+ coll.checkIterAddAllowed();
+ try {
+ OperationStatus status = OperationStatus.SUCCESS;
+ if (toNext != 0 && toPrevious != 0) { // database is empty
+ if (coll.view.keysRenumbered) { // recno-renumber database
+ /*
+ * Close cursor during append and then reopen to support
+ * CDB restriction that append may not be called with a
+ * cursor open; note the append will still fail if the
+ * application has another cursor open.
+ */
+ close();
+ status = coll.view.append(value, null, null);
+ cursor = new DataCursor(coll.view, writeAllowed);
+ reset();
+ next(); // move past new record
+ } else { // hash/btree with duplicates
+ throw new IllegalStateException(
+ "Collection is empty, cannot add() duplicate");
+ }
+ } else { // database is not empty
+ boolean putBefore = false;
+ if (coll.view.keysRenumbered) { // recno-renumber database
+ moveToCurrent();
+ if (hasNext()) {
+ status = cursor.putBefore(value);
+ putBefore = true;
+ } else {
+ status = cursor.putAfter(value);
+ }
+ } else { // hash/btree with duplicates
+ if (coll.areDuplicatesOrdered()) {
+ status = cursor.putNoDupData(null, value, null, true);
+ } else if (toNext == 0) {
+ status = cursor.putBefore(value);
+ putBefore = true;
+ } else {
+ status = cursor.putAfter(value);
+ }
+ }
+ if (putBefore) {
+ toPrevious = 0;
+ toNext = MOVE_NEXT;
+ }
+ }
+ if (status == OperationStatus.KEYEXIST) {
+ throw new IllegalArgumentException("Duplicate value");
+ } else if (status != OperationStatus.SUCCESS) {
+ throw new IllegalArgumentException("Could not insert: " +
+ status);
+ }
+ setAndRemoveAllowed = false;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ // --- end Iterator/ListIterator methods ---
+
+ /**
+ * Resets cursor to an uninitialized state.
+ */
+ private void reset() {
+
+ toNext = MOVE_FIRST;
+ toPrevious = MOVE_PREV;
+ toCurrent = 0;
+ currentData = null;
+ /*
+ * Initialize cursor at beginning to avoid "initial previous == last"
+ * behavior when cursor is uninitialized.
+ *
+ * FindBugs whines about us ignoring the return value from hasNext().
+ */
+ hasNext();
+ }
+
+ /**
+ * Returns the number of elements having the same key value as the key
+ * value of the element last returned by next() or previous(). If no
+ * duplicates are allowed, 1 is always returned.
+ *
+ * @return the number of duplicates.
+ *
+ * @throws IllegalStateException if next() or previous() has not been
+ * called for this iterator, or if remove() or add() were called after
+ * the last call to next() or previous().
+ */
+ public int count() {
+
+ if (!setAndRemoveAllowed) throw new IllegalStateException();
+ try {
+ moveToCurrent();
+ return cursor.count();
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Closes this iterator.
+ * This method does not exist in the standard {@link Iterator} or {@link
+ * ListIterator} interfaces.
+ *
+ * <p>After being closed, only the {@link #hasNext} and {@link
+ * #hasPrevious} methods may be called and these will return false. {@link
+ * #close()} may also be called again and will do nothing. If other
+ * methods are called a <code>NullPointerException</code> will generally be
+ * thrown.</p>
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public void close() {
+
+ if (cursor != null) {
+ coll.closeCursor(cursor);
+ cursor = null;
+ }
+ }
+
+ /**
+ * Returns the collection associated with this iterator.
+ * This method does not exist in the standard {@link Iterator} or {@link
+ * ListIterator} interfaces.
+ *
+ * @return the collection associated with this iterator.
+ */
+ public final StoredCollection getCollection() {
+
+ return coll;
+ }
+
+ final boolean isCurrentData(Object currentData) {
+
+ return (this.currentData == currentData);
+ }
+
+ final boolean moveToIndex(int index) {
+
+ try {
+ OperationStatus status =
+ cursor.getSearchKey(new Integer(index), null, lockForWrite);
+ setAndRemoveAllowed = (status == OperationStatus.SUCCESS);
+ return setAndRemoveAllowed;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ private void moveToCurrent()
+ throws DatabaseException {
+
+ if (toCurrent != 0) {
+ move(toCurrent);
+ toCurrent = 0;
+ }
+ }
+
+ private OperationStatus move(int direction)
+ throws DatabaseException {
+
+ switch (direction) {
+ case MOVE_NEXT:
+ if (coll.iterateDuplicates()) {
+ return cursor.getNext(lockForWrite);
+ } else {
+ return cursor.getNextNoDup(lockForWrite);
+ }
+ case MOVE_PREV:
+ if (coll.iterateDuplicates()) {
+ return cursor.getPrev(lockForWrite);
+ } else {
+ return cursor.getPrevNoDup(lockForWrite);
+ }
+ case MOVE_FIRST:
+ return cursor.getFirst(lockForWrite);
+ default:
+ throw new IllegalArgumentException(String.valueOf(direction));
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredKeySet.java b/db/java/src/com/sleepycat/collections/StoredKeySet.java
new file mode 100644
index 000000000..1c86aa003
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredKeySet.java
@@ -0,0 +1,144 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredKeySet.java,v 1.3 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Set;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.OperationStatus;
+
+/**
+ * The Set returned by Map.keySet() and which can also be constructed directly
+ * if a Map is not needed.
+ * Since this collection is a set it only contains one element for each key,
+ * even when duplicates are allowed. Key set iterators are therefore
+ * particularly useful for enumerating the unique keys of a store or index that
+ * allows duplicates.
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredKeySet extends StoredCollection implements Set {
+
+ /**
+ * Creates a key set view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public StoredKeySet(Database database, EntryBinding keyBinding,
+ boolean writeAllowed) {
+
+ super(new DataView(database, keyBinding, null, null,
+ writeAllowed, null));
+ }
+
+ StoredKeySet(DataView keySetView) {
+
+ super(keySetView);
+ }
+
+ /**
+ * Adds the specified key to this set if it is not already present
+ * (optional operation).
+ * When a key is added the value in the underlying data store will be
+ * empty.
+ * This method conforms to the {@link Set#add} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is indexed, or
+ * if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean add(Object key) {
+
+ DataCursor cursor = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ cursor = new DataCursor(view, true);
+ OperationStatus status = cursor.putNoOverwrite(key, null, false);
+ closeCursor(cursor);
+ commitAutoCommit(doAutoCommit);
+ return (status == OperationStatus.SUCCESS);
+ } catch (Exception e) {
+ closeCursor(cursor);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Removes the specified key from this set if it is present (optional
+ * operation).
+ * If duplicates are allowed, this method removes all duplicates for the
+ * given key.
+ * This method conforms to the {@link Set#remove} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean remove(Object key) {
+
+ return removeKey(key, null);
+ }
+
+ /**
+ * Returns true if this set contains the specified key.
+ * This method conforms to the {@link Set#contains} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean contains(Object key) {
+
+ return containsKey(key);
+ }
+
+ boolean hasValues() {
+
+ return false;
+ }
+
+ Object makeIteratorData(StoredIterator iterator, DataCursor cursor)
+ throws DatabaseException {
+
+ return cursor.getCurrentKey();
+ }
+
+ boolean iterateDuplicates() {
+
+ return false;
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredList.java b/db/java/src/com/sleepycat/collections/StoredList.java
new file mode 100644
index 000000000..a65e5a734
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredList.java
@@ -0,0 +1,604 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredList.java,v 1.4 2004/08/02 18:52:05 mjc Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.OperationStatus;
+
+/**
+ * A List view of a {@link Database}.
+ *
+ * <p>For all stored lists the keys of the underlying Database
+ * must have record number format, and therefore the store or index must be a
+ * RECNO, RECNO-RENUMBER, QUEUE, or BTREE-RECNUM database. Only RECNO-RENUMBER
+ * allows true list behavior where record numbers are renumbered following the
+ * position of an element that is added or removed. For the other access
+ * methods (RECNO, QUEUE, and BTREE-RECNUM), stored Lists are most useful as
+ * read-only collections where record numbers are not required to be
+ * sequential.</p>
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * <p>In addition to the standard List methods, this class provides the
+ * following methods for stored lists only. Note that the use of these methods
+ * is not compatible with the standard Java collections interface.</p>
+ * <ul>
+ * <li>{@link #append(Object)}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredList extends StoredCollection implements List {
+
+ private static final EntryBinding DEFAULT_KEY_BINDING =
+ new IndexKeyBinding(1);
+
+ private int baseIndex = 1;
+ private boolean isSubList;
+
+ /**
+ * Creates a list view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param valueBinding is the binding used to translate between value
+ * buffers and value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public StoredList(Database database, EntryBinding valueBinding,
+ boolean writeAllowed) {
+
+ super(new DataView(database, DEFAULT_KEY_BINDING, valueBinding, null,
+ writeAllowed, null));
+ }
+
+ /**
+ * Creates a list entity view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param valueEntityBinding is the binding used to translate between
+ * key/value buffers and entity value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public StoredList(Database database, EntityBinding valueEntityBinding,
+ boolean writeAllowed) {
+
+ super(new DataView(database, DEFAULT_KEY_BINDING, null,
+ valueEntityBinding, writeAllowed, null));
+ }
+
+ /**
+ * Creates a list view of a {@link Database} with a {@link
+ * PrimaryKeyAssigner}. Writing is allowed for the created list.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param valueBinding is the binding used to translate between value
+ * buffers and value objects.
+ *
+ * @param keyAssigner is used by the {@link #add} and {@link #append}
+ * methods to assign primary keys.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public StoredList(Database database, EntryBinding valueBinding,
+ PrimaryKeyAssigner keyAssigner) {
+
+ super(new DataView(database, DEFAULT_KEY_BINDING, valueBinding,
+ null, true, keyAssigner));
+ }
+
+ /**
+ * Creates a list entity view of a {@link Database} with a {@link
+ * PrimaryKeyAssigner}. Writing is allowed for the created list.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param valueEntityBinding is the binding used to translate between
+ * key/value buffers and entity value objects.
+ *
+ * @param keyAssigner is used by the {@link #add} and {@link #append}
+ * methods to assign primary keys.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public StoredList(Database database, EntityBinding valueEntityBinding,
+ PrimaryKeyAssigner keyAssigner) {
+
+ super(new DataView(database, DEFAULT_KEY_BINDING, null,
+ valueEntityBinding, true, keyAssigner));
+ }
+
+ private StoredList(DataView view, int baseIndex) {
+
+ super(view);
+ this.baseIndex = baseIndex;
+ this.isSubList = true;
+ }
+
+ /**
+ * Inserts the specified element at the specified position in this list
+ * (optional operation).
+ * This method conforms to the {@link List#add(int, Object)} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is a sublist, or
+ * if the collection is indexed, or if the collection is read-only, or if
+ * the RECNO-RENUMBER access method was not used.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public void add(int index, Object value) {
+
+ checkIterAddAllowed();
+ DataCursor cursor = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ cursor = new DataCursor(view, true);
+ OperationStatus status =
+ cursor.getSearchKey(new Long(index), null, false);
+ if (status == OperationStatus.SUCCESS) {
+ cursor.putBefore(value);
+ closeCursor(cursor);
+ } else {
+ closeCursor(cursor);
+ cursor = null;
+ view.append(value, null, null);
+ }
+ commitAutoCommit(doAutoCommit);
+ } catch (Exception e) {
+ closeCursor(cursor);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Appends the specified element to the end of this list (optional
+ * operation).
+ * This method conforms to the {@link List#add(Object)} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is a sublist, or
+ * if the collection is indexed, or if the collection is read-only, or if
+ * the RECNO-RENUMBER access method was not used.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean add(Object value) {
+
+ checkIterAddAllowed();
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ view.append(value, null, null);
+ commitAutoCommit(doAutoCommit);
+ return true;
+ } catch (Exception e) {
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Appends a given value returning the newly assigned index.
+ * If a {@link com.sleepycat.collections.PrimaryKeyAssigner} is associated
+ * with Store for this list, it will be used to assigned the returned
+ * index. Otherwise the Store must be a QUEUE or RECNO database and the
+ * next available record number is assigned as the index. This method does
+ * not exist in the standard {@link List} interface.
+ *
+ * @param value the value to be appended.
+ *
+ * @return the assigned index.
+ *
+ * @throws UnsupportedOperationException if the collection is indexed, or
+ * if the collection is read-only, or if the Store has no {@link
+ * com.sleepycat.collections.PrimaryKeyAssigner} and is not a QUEUE or
+ * RECNO database.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public int append(Object value) {
+
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ Object[] key = new Object[1];
+ view.append(value, key, null);
+ commitAutoCommit(doAutoCommit);
+ return ((Number) key[0]).intValue();
+ } catch (Exception e) {
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ void checkIterAddAllowed()
+ throws UnsupportedOperationException {
+
+ if (isSubList) {
+ throw new UnsupportedOperationException("cannot add to subList");
+ }
+ if (!view.keysRenumbered) { // RECNO-RENUM
+ throw new UnsupportedOperationException(
+ "requires renumbered keys");
+ }
+ }
+
+ /**
+ * Inserts all of the elements in the specified collection into this list
+ * at the specified position (optional operation).
+ * This method conforms to the {@link List#addAll(int, Collection)}
+ * interface.
+ *
+ * @throws UnsupportedOperationException if the collection is a sublist, or
+ * if the collection is indexed, or if the collection is read-only, or if
+ * the RECNO-RENUMBER access method was not used.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean addAll(int index, Collection coll) {
+
+ checkIterAddAllowed();
+ DataCursor cursor = null;
+ Iterator i = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ i = coll.iterator();
+ if (!i.hasNext()) {
+ return false;
+ }
+ cursor = new DataCursor(view, true);
+ OperationStatus status =
+ cursor.getSearchKey(new Long(index), null, false);
+ if (status == OperationStatus.SUCCESS) {
+ while (i.hasNext()) {
+ cursor.putBefore(i.next());
+ }
+ closeCursor(cursor);
+ } else {
+ closeCursor(cursor);
+ cursor = null;
+ while (i.hasNext()) {
+ view.append(i.next(), null, null);
+ }
+ }
+ StoredIterator.close(i);
+ commitAutoCommit(doAutoCommit);
+ return true;
+ } catch (Exception e) {
+ closeCursor(cursor);
+ StoredIterator.close(i);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Returns true if this list contains the specified element.
+ * This method conforms to the {@link List#contains} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean contains(Object value) {
+
+ return containsValue(value);
+ }
+
+ /**
+ * Returns the element at the specified position in this list.
+ * This method conforms to the {@link List#get} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public Object get(int index) {
+
+ return super.get(new Long(index));
+ }
+
+ /**
+ * Returns the index in this list of the first occurrence of the specified
+ * element, or -1 if this list does not contain this element.
+ * This method conforms to the {@link List#indexOf} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public int indexOf(Object value) {
+
+ return indexOf(value, true);
+ }
+
+ /**
+ * Returns the index in this list of the last occurrence of the specified
+ * element, or -1 if this list does not contain this element.
+ * This method conforms to the {@link List#lastIndexOf} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public int lastIndexOf(Object value) {
+
+ return indexOf(value, false);
+ }
+
+ private int indexOf(Object value, boolean findFirst) {
+
+ DataCursor cursor = null;
+ try {
+ cursor = new DataCursor(view, false);
+ OperationStatus status = cursor.find(value, findFirst);
+ return (status == OperationStatus.SUCCESS)
+ ? (cursor.getCurrentRecordNumber() - baseIndex)
+ : (-1);
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ } finally {
+ closeCursor(cursor);
+ }
+ }
+
+ int getIndexOffset() {
+
+ return baseIndex;
+ }
+
+ /**
+ * Returns a list iterator of the elements in this list (in proper
+ * sequence).
+ * The iterator will be read-only if the collection is read-only.
+ * This method conforms to the {@link List#listIterator()} interface.
+ *
+ * @return a {@link StoredIterator} for this collection.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ *
+ * @see #isWriteAllowed
+ */
+ public ListIterator listIterator() {
+
+ return iterator(isWriteAllowed());
+ }
+
+ /**
+ * Returns a list iterator of the elements in this list (in proper
+ * sequence), starting at the specified position in this list.
+ * The iterator will be read-only if the collection is read-only.
+ * This method conforms to the {@link List#listIterator(int)} interface.
+ *
+ * @return a {@link StoredIterator} for this collection.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ *
+ * @see #isWriteAllowed
+ */
+ public ListIterator listIterator(int index) {
+
+ StoredIterator i = iterator(isWriteAllowed());
+ if (i.moveToIndex(index)) {
+ return i;
+ } else {
+ i.close();
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ }
+
+ /**
+ * Removes the element at the specified position in this list (optional
+ * operation).
+ * This method conforms to the {@link List#remove(int)} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is a sublist, or
+ * if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public Object remove(int index) {
+
+ try {
+ Object[] oldVal = new Object[1];
+ removeKey(new Long(index), oldVal);
+ return oldVal[0];
+ } catch (IllegalArgumentException e) {
+ throw new IndexOutOfBoundsException(e.getMessage());
+ }
+ }
+
+ /**
+ * Removes the first occurrence in this list of the specified element
+ * (optional operation).
+ * This method conforms to the {@link List#remove(Object)} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is a sublist, or
+ * if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean remove(Object value) {
+
+ return removeValue(value);
+ }
+
+ /**
+ * Replaces the element at the specified position in this list with the
+ * specified element (optional operation).
+ * This method conforms to the {@link List#set} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is indexed, or
+ * if the collection is read-only.
+ *
+ * @throws IllegalArgumentException if an entity value binding is used and
+ * the primary key of the value given is different than the existing stored
+ * primary key.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public Object set(int index, Object value) {
+
+ try {
+ return put(new Long(index), value);
+ } catch (IllegalArgumentException e) {
+ throw new IndexOutOfBoundsException(e.getMessage());
+ }
+ }
+
+ /**
+ * Returns a view of the portion of this list between the specified
+ * fromIndex, inclusive, and toIndex, exclusive.
+ * Note that add() and remove() may not be called for the returned sublist.
+ * This method conforms to the {@link List#subList} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public List subList(int fromIndex, int toIndex) {
+
+ if (fromIndex < 0 || fromIndex > toIndex) {
+ throw new IndexOutOfBoundsException(String.valueOf(fromIndex));
+ }
+ try {
+ int newBaseIndex = baseIndex + fromIndex;
+ return new StoredList(
+ view.subView(new Long(fromIndex), true,
+ new Long(toIndex), false,
+ new IndexKeyBinding(newBaseIndex)),
+ newBaseIndex);
+ } catch (KeyRangeException e) {
+ throw new IndexOutOfBoundsException(e.getMessage());
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Compares the specified object with this list for equality.
+ * A value comparison is performed by this method and the stored values
+ * are compared rather than calling the equals() method of each element.
+ * This method conforms to the {@link List#equals} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean equals(Object other) {
+
+ if (!(other instanceof List)) return false;
+ List otherList = (List) other;
+ ListIterator i1 = null;
+ ListIterator i2 = null;
+ try {
+ i1 = listIterator();
+ i2 = otherList.listIterator();
+ while (i1.hasNext()) {
+ if (!i2.hasNext()) return false;
+ if (i1.nextIndex() != i2.nextIndex()) return false;
+ Object o1 = i1.next();
+ Object o2 = i2.next();
+ if (o1 == null) {
+ if (o2 != null) return false;
+ } else {
+ if (!o1.equals(o2)) return false;
+ }
+ }
+ if (i2.hasNext()) return false;
+ return true;
+ } finally {
+ StoredIterator.close(i1);
+ StoredIterator.close(i2);
+ }
+ }
+
+ /*
+ * Add this in to keep FindBugs from whining at us about implementing
+ * equals(), but not hashCode().
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ Object makeIteratorData(StoredIterator iterator, DataCursor cursor)
+ throws DatabaseException {
+
+ return cursor.getCurrentValue();
+ }
+
+ boolean hasValues() {
+
+ return true;
+ }
+
+ private static class IndexKeyBinding extends RecordNumberBinding {
+
+ private int baseIndex;
+
+ private IndexKeyBinding(int baseIndex) {
+
+ this.baseIndex = baseIndex;
+ }
+
+ public Object entryToObject(DatabaseEntry data) {
+
+ return new Long(entryToRecordNumber(data) - baseIndex);
+ }
+
+ public void objectToEntry(Object object, DatabaseEntry data) {
+
+ recordNumberToEntry(((Number) object).intValue() + baseIndex,
+ data);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredMap.java b/db/java/src/com/sleepycat/collections/StoredMap.java
new file mode 100644
index 000000000..1c0e1dcf1
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredMap.java
@@ -0,0 +1,511 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredMap.java,v 1.4 2004/09/22 18:01:03 bostic Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.Database;
+
+/**
+ * A Map view of a {@link Database}.
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * <p>In addition to the standard Map methods, this class provides the
+ * following methods for stored maps only. Note that the use of these methods
+ * is not compatible with the standard Java collections interface.</p>
+ * <ul>
+ * <li>{@link #duplicates(Object)}</li>
+ * <li>{@link #append(Object)}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredMap extends StoredContainer implements Map {
+
+ private StoredKeySet keySet;
+ private boolean keySetInitialized = false;
+ private StoredEntrySet entrySet;
+ private boolean entrySetInitialized = false;
+ private StoredValueSet valueSet;
+ private boolean valueSetInitialized = false;
+
+ /**
+ * Creates a map view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param valueBinding is the binding used to translate between value
+ * buffers and value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredMap(Database database, EntryBinding keyBinding,
+ EntryBinding valueBinding, boolean writeAllowed) {
+
+ super(new DataView(database, keyBinding, valueBinding, null,
+ writeAllowed, null));
+ }
+
+ /**
+ * Creates a map view of a {@link Database} with a {@link
+ * PrimaryKeyAssigner}. Writing is allowed for the created map.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param valueBinding is the binding used to translate between value
+ * buffers and value objects.
+ *
+ * @param keyAssigner is used by the {@link #append} method to assign
+ * primary keys.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredMap(Database database, EntryBinding keyBinding,
+ EntryBinding valueBinding,
+ PrimaryKeyAssigner keyAssigner) {
+
+ super(new DataView(database, keyBinding, valueBinding, null,
+ true, keyAssigner));
+ }
+
+ protected Object clone()
+ throws CloneNotSupportedException {
+
+ // cached collections must be cleared and recreated with the new view
+ // of the map to inherit the new view's properties
+ StoredMap other = (StoredMap) super.clone();
+ other.keySet = null;
+ other.keySetInitialized = false;
+ other.entrySet = null;
+ other.entrySetInitialized = false;
+ other.valueSet = null;
+ other.valueSetInitialized = false;
+ return other;
+ }
+
+ /**
+ * Creates a map entity view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param valueEntityBinding is the binding used to translate between
+ * key/value buffers and entity value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredMap(Database database, EntryBinding keyBinding,
+ EntityBinding valueEntityBinding, boolean writeAllowed) {
+
+ super(new DataView(database, keyBinding, null, valueEntityBinding,
+ writeAllowed, null));
+ }
+
+ /**
+ * Creates a map entity view of a {@link Database} with a {@link
+ * PrimaryKeyAssigner}. Writing is allowed for the created map.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param valueEntityBinding is the binding used to translate between
+ * key/value buffers and entity value objects.
+ *
+ * @param keyAssigner is used by the {@link #append} method to assign
+ * primary keys.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredMap(Database database, EntryBinding keyBinding,
+ EntityBinding valueEntityBinding,
+ PrimaryKeyAssigner keyAssigner) {
+
+ super(new DataView(database, keyBinding, null, valueEntityBinding,
+ true, keyAssigner));
+ }
+
+ StoredMap(DataView view) {
+
+ super(view);
+ }
+
+ /**
+ * Returns the value to which this map maps the specified key. If
+ * duplicates are allowed, this method returns the first duplicate, in the
+ * order in which duplicates are configured, that maps to the specified
+ * key.
+ *
+ * This method conforms to the {@link Map#get} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object get(Object key) {
+
+ return super.get(key);
+ }
+
+ /**
+ * Associates the specified value with the specified key in this map
+ * (optional operation). If duplicates are allowed and the specified key
+ * is already mapped to a value, this method appends the new duplicate
+ * after the existing duplicates. This method conforms to the {@link
+ * Map#put} interface.
+ *
+ * <p>The key parameter may be null if an entity binding is used and the
+ * key will be derived from the value (entity) parameter. If an entity
+ * binding is used and the key parameter is non-null, then the key
+ * parameter must be equal to the key derived from the value parameter.</p>
+ *
+ * @return the previous value associated with specified key, or null if
+ * there was no mapping for the key or if duplicates are allowed.
+ *
+ * @throws UnsupportedOperationException if the collection is indexed, or
+ * if the collection is read-only.
+ *
+ * @throws IllegalArgumentException if an entity value binding is used and
+ * the primary key of the value given is different than the existing stored
+ * primary key.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object put(Object key, Object value) {
+
+ return super.put(key, value);
+ }
+
+ /**
+ * Appends a given value returning the newly assigned key. If a {@link
+ * PrimaryKeyAssigner} is associated with Store for this map, it will be
+ * used to assigned the returned key. Otherwise the Store must be a QUEUE
+ * or RECNO database and the next available record number is assigned as
+ * the key. This method does not exist in the standard {@link Map}
+ * interface.
+ *
+ * @param value the value to be appended.
+ *
+ * @return the assigned key.
+ *
+ * @throws UnsupportedOperationException if the collection is indexed, or
+ * if the collection is read-only, or if the Store has no {@link
+ * PrimaryKeyAssigner} and is not a QUEUE or RECNO database.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object append(Object value) {
+
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ Object[] key = new Object[1];
+ view.append(value, key, null);
+ commitAutoCommit(doAutoCommit);
+ return key[0];
+ } catch (Exception e) {
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Removes the mapping for this key from this map if present (optional
+ * operation). If duplicates are allowed, this method removes all
+ * duplicates for the given key. This method conforms to the {@link
+ * Map#remove} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object remove(Object key) {
+
+ Object[] oldVal = new Object[1];
+ removeKey(key, oldVal);
+ return oldVal[0];
+ }
+
+ /**
+ * Returns true if this map contains the specified key. This method
+ * conforms to the {@link Map#containsKey} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public boolean containsKey(Object key) {
+
+ return super.containsKey(key);
+ }
+
+ /**
+ * Returns true if this map contains the specified value. When an entity
+ * binding is used, this method returns whether the map contains the
+ * primary key and value mapping of the entity. This method conforms to
+ * the {@link Map#containsValue} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public boolean containsValue(Object value) {
+
+ return super.containsValue(value);
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this map (optional
+ * operation). When duplicates are allowed, the mappings in the specified
+ * map are effectively appended to the existing mappings in this map, that
+ * is no previously existing mappings in this map are replaced. This
+ * method conforms to the {@link Map#putAll} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only, or
+ * if the collection is indexed.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public void putAll(Map map) {
+
+ boolean doAutoCommit = beginAutoCommit();
+ Iterator entries = null;
+ try {
+ entries = map.entrySet().iterator();
+ while (entries.hasNext()) {
+ Map.Entry entry = (Map.Entry) entries.next();
+ put(entry.getKey(), entry.getValue());
+ }
+ StoredIterator.close(entries);
+ commitAutoCommit(doAutoCommit);
+ } catch (Exception e) {
+ StoredIterator.close(entries);
+ throw handleException(e, doAutoCommit);
+ }
+ }
+
+ /**
+ * Returns a set view of the keys contained in this map. A {@link
+ * java.util.SortedSet} is returned if the map is ordered. The returned
+ * collection will be read-only if the map is read-only. This method
+ * conforms to the {@link Map#keySet()} interface.
+ *
+ * @return a {@link StoredKeySet} or a {@link StoredSortedKeySet} for this
+ * map.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ *
+ * @see #isOrdered
+ * @see #isWriteAllowed
+ */
+ public Set keySet() {
+
+ if (!keySetInitialized) {
+ synchronized (this) {
+ if (!keySetInitialized) {
+ DataView newView = view.keySetView();
+ if (isOrdered()) {
+ keySet = new StoredSortedKeySet(newView);
+ } else {
+ keySet = new StoredKeySet(newView);
+ }
+ keySetInitialized = true;
+ }
+ }
+ }
+ return keySet;
+ }
+
+ /**
+ * Returns a set view of the mappings contained in this map. A {@link
+ * java.util.SortedSet} is returned if the map is ordered. The returned
+ * collection will be read-only if the map is read-only. This method
+ * conforms to the {@link Map#entrySet()} interface.
+ *
+ * @return a {@link StoredEntrySet} or a {@link StoredSortedEntrySet} for
+ * this map.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ *
+ * @see #isOrdered
+ * @see #isWriteAllowed
+ */
+ public Set entrySet() {
+
+ if (!entrySetInitialized) {
+ synchronized (this) {
+ if (!entrySetInitialized) {
+ if (isOrdered()) {
+ entrySet = new StoredSortedEntrySet(view);
+ } else {
+ entrySet = new StoredEntrySet(view);
+ }
+ entrySetInitialized = true;
+ }
+ }
+ }
+ return entrySet;
+ }
+
+ /**
+ * Returns a collection view of the values contained in this map. A {@link
+ * java.util.SortedSet} is returned if the map is ordered and the
+ * value/entity binding can be used to derive the map's key from its
+ * value/entity object. The returned collection will be read-only if the
+ * map is read-only. This method conforms to the {@link Map#entrySet()}
+ * interface.
+ *
+ * @return a {@link StoredValueSet} or a {@link StoredSortedValueSet} for
+ * this map.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ *
+ * @see #isOrdered
+ * @see #isWriteAllowed
+ */
+ public Collection values() {
+
+ if (!valueSetInitialized) {
+ synchronized (this) {
+ if (!valueSetInitialized) {
+ DataView newView = view.valueSetView();
+ if (isOrdered() && newView.canDeriveKeyFromValue()) {
+ valueSet = new StoredSortedValueSet(newView);
+ } else {
+ valueSet = new StoredValueSet(newView);
+ }
+ valueSetInitialized = true;
+ }
+ }
+ }
+ return valueSet;
+ }
+
+ /**
+ * Returns a new collection containing the values mapped to the given key
+ * in this map. This collection's iterator() method is particularly useful
+ * for iterating over the duplicates for a given key, since this is not
+ * supported by the standard Map interface. This method does not exist in
+ * the standard {@link Map} interface.
+ *
+ * <p>If no mapping for the given key is present, an empty collection is
+ * returned. If duplicates are not allowed, at most a single value will be
+ * in the collection returned. If duplicates are allowed, the returned
+ * collection's add() method may be used to add values for the given
+ * key.</p>
+ *
+ * @param key is the key for which values are to be returned.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Collection duplicates(Object key) {
+
+ try {
+ DataView newView = view.valueSetView(key);
+ return new StoredValueSet(newView, true);
+ } catch (KeyRangeException e) {
+ return Collections.EMPTY_SET;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+
+ /**
+ * Compares the specified object with this map for equality. A value
+ * comparison is performed by this method and the stored values are
+ * compared rather than calling the equals() method of each element. This
+ * method conforms to the {@link Map#equals} interface.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public boolean equals(Object other) {
+
+ if (other instanceof Map) {
+ return entrySet().equals(((Map) other).entrySet());
+ } else {
+ return false;
+ }
+ }
+
+ /*
+ * Add this in to keep FindBugs from whining at us about implementing
+ * equals(), but not hashCode().
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ /**
+ * Converts the map to a string representation for debugging. WARNING: All
+ * mappings will be converted to strings and returned and therefore the
+ * returned string may be very large.
+ *
+ * @return the string representation.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public String toString() {
+
+ return entrySet().toString();
+ }
+}
+
diff --git a/db/java/src/com/sleepycat/collections/StoredMapEntry.java b/db/java/src/com/sleepycat/collections/StoredMapEntry.java
new file mode 100644
index 000000000..020e878e4
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredMapEntry.java
@@ -0,0 +1,41 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredMapEntry.java,v 1.1 2004/04/09 16:34:09 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+/**
+ * @author Mark Hayes
+ */
+final class StoredMapEntry extends MapEntryParameter {
+
+ private StoredIterator iter;
+ private StoredCollection coll;
+
+ StoredMapEntry(Object key, Object value, StoredCollection coll,
+ StoredIterator iter) {
+
+ super(key, value);
+ // Assert: coll, coll.keyBinding/valueBinding
+ this.coll = coll;
+ this.iter = iter;
+ }
+
+ public Object setValue(Object newValue) {
+
+ Object oldValue;
+ if (iter != null && iter.isCurrentData(this)) {
+ oldValue = getValue();
+ iter.set(newValue);
+ } else {
+ oldValue = coll.put(getKey(), newValue);
+ }
+ setValueInternal(newValue);
+ return oldValue;
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredSortedEntrySet.java b/db/java/src/com/sleepycat/collections/StoredSortedEntrySet.java
new file mode 100644
index 000000000..99fceab9b
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredSortedEntrySet.java
@@ -0,0 +1,220 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredSortedEntrySet.java,v 1.2 2004/06/02 20:59:39 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.SortedSet;
+
+/**
+ * The SortedSet returned by Map.entrySet(). This class may not be
+ * instantiated directly. Contrary to what is stated by {@link Map#entrySet}
+ * this class does support the {@link #add} and {@link #addAll} methods.
+ *
+ * <p>The {@link java.util.Map.Entry#setValue} method of the Map.Entry objects
+ * that are returned by this class and its iterators behaves just as the {@link
+ * StoredIterator#set} method does.</p>
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * <p>In addition to the standard SortedSet methods, this class provides the
+ * following methods for stored sorted sets only. Note that the use of these
+ * methods is not compatible with the standard Java collections interface.</p>
+ * <ul>
+ * <li>{@link #headSet(Object, boolean)}</li>
+ * <li>{@link #tailSet(Object, boolean)}</li>
+ * <li>{@link #subSet(Object, boolean, Object, boolean)}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredSortedEntrySet extends StoredEntrySet implements SortedSet {
+
+ StoredSortedEntrySet(DataView mapView) {
+
+ super(mapView);
+ }
+
+ /**
+ * Returns null since comparators are not supported. The natural ordering
+ * of a stored collection is data byte order, whether the data classes
+ * implement the {@link java.lang.Comparable} interface or not.
+ * This method does not conform to the {@link SortedSet#comparator}
+ * interface.
+ *
+ * @return null.
+ */
+ public Comparator comparator() {
+
+ return null;
+ }
+
+ /**
+ * Returns the first (lowest) element currently in this sorted set.
+ * This method conforms to the {@link SortedSet#first} interface.
+ *
+ * @return the first element.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object first() {
+
+ return getFirstOrLast(true);
+ }
+
+ /**
+ * Returns the last (highest) element currently in this sorted set.
+ * This method conforms to the {@link SortedSet#last} interface.
+ *
+ * @return the last element.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object last() {
+
+ return getFirstOrLast(false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly less than toMapEntry.
+ * This method conforms to the {@link SortedSet#headSet} interface.
+ *
+ * @param toMapEntry the upper bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet headSet(Object toMapEntry) {
+
+ return subSet(null, false, toMapEntry, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly less than toMapEntry, optionally including toMapEntry.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param toMapEntry is the upper bound.
+ *
+ * @param toInclusive is true to include toMapEntry.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet headSet(Object toMapEntry, boolean toInclusive) {
+
+ return subSet(null, false, toMapEntry, toInclusive);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * greater than or equal to fromMapEntry.
+ * This method conforms to the {@link SortedSet#tailSet} interface.
+ *
+ * @param fromMapEntry is the lower bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet tailSet(Object fromMapEntry) {
+
+ return subSet(fromMapEntry, true, null, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly greater than fromMapEntry, optionally including fromMapEntry.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param fromMapEntry is the lower bound.
+ *
+ * @param fromInclusive is true to include fromMapEntry.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet tailSet(Object fromMapEntry, boolean fromInclusive) {
+
+ return subSet(fromMapEntry, fromInclusive, null, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements range
+ * from fromMapEntry, inclusive, to toMapEntry, exclusive.
+ * This method conforms to the {@link SortedSet#subSet} interface.
+ *
+ * @param fromMapEntry is the lower bound.
+ *
+ * @param toMapEntry is the upper bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet subSet(Object fromMapEntry, Object toMapEntry) {
+
+ return subSet(fromMapEntry, true, toMapEntry, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly greater than fromMapEntry and strictly less than toMapEntry,
+ * optionally including fromMapEntry and toMapEntry.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param fromMapEntry is the lower bound.
+ *
+ * @param fromInclusive is true to include fromMapEntry.
+ *
+ * @param toMapEntry is the upper bound.
+ *
+ * @param toInclusive is true to include toMapEntry.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet subSet(Object fromMapEntry, boolean fromInclusive,
+ Object toMapEntry, boolean toInclusive) {
+
+ Object fromKey = (fromMapEntry != null) ?
+ ((Map.Entry) fromMapEntry).getKey() : null;
+ Object toKey = (toMapEntry != null) ?
+ ((Map.Entry) toMapEntry).getKey() : null;
+ try {
+ return new StoredSortedEntrySet(
+ view.subView(fromKey, fromInclusive, toKey, toInclusive, null));
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredSortedKeySet.java b/db/java/src/com/sleepycat/collections/StoredSortedKeySet.java
new file mode 100644
index 000000000..44b12d55a
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredSortedKeySet.java
@@ -0,0 +1,241 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredSortedKeySet.java,v 1.2 2004/06/02 20:59:39 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Comparator;
+import java.util.SortedSet;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.Database;
+
+/**
+ * The SortedSet returned by Map.keySet() and which can also be constructed
+ * directly if a Map is not needed.
+ * Since this collection is a set it only contains one element for each key,
+ * even when duplicates are allowed. Key set iterators are therefore
+ * particularly useful for enumerating the unique keys of a store or index that
+ * allows duplicates.
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * <p>In addition to the standard SortedSet methods, this class provides the
+ * following methods for stored sorted sets only. Note that the use of these
+ * methods is not compatible with the standard Java collections interface.</p>
+ * <ul>
+ * <li>{@link #headSet(Object, boolean)}</li>
+ * <li>{@link #tailSet(Object, boolean)}</li>
+ * <li>{@link #subSet(Object, boolean, Object, boolean)}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredSortedKeySet extends StoredKeySet implements SortedSet {
+
+ /**
+ * Creates a sorted key set view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredSortedKeySet(Database database, EntryBinding keyBinding,
+ boolean writeAllowed) {
+
+ super(new DataView(database, keyBinding, null, null,
+ writeAllowed, null));
+ }
+
+ StoredSortedKeySet(DataView keySetView) {
+
+ super(keySetView);
+ }
+
+ /**
+ * Returns null since comparators are not supported. The natural ordering
+ * of a stored collection is data byte order, whether the data classes
+ * implement the {@link java.lang.Comparable} interface or not.
+ * This method does not conform to the {@link SortedSet#comparator}
+ * interface.
+ *
+ * @return null.
+ */
+ public Comparator comparator() {
+
+ return null;
+ }
+
+ /**
+ * Returns the first (lowest) element currently in this sorted set.
+ * This method conforms to the {@link SortedSet#first} interface.
+ *
+ * @return the first element.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object first() {
+
+ return getFirstOrLast(true);
+ }
+
+ /**
+ * Returns the last (highest) element currently in this sorted set.
+ * This method conforms to the {@link SortedSet#last} interface.
+ *
+ * @return the last element.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object last() {
+
+ return getFirstOrLast(false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly less than toKey.
+ * This method conforms to the {@link SortedSet#headSet} interface.
+ *
+ * @param toKey is the upper bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet headSet(Object toKey) {
+
+ return subSet(null, false, toKey, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly less than toKey, optionally including toKey.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param toKey is the upper bound.
+ *
+ * @param toInclusive is true to include toKey.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet headSet(Object toKey, boolean toInclusive) {
+
+ return subSet(null, false, toKey, toInclusive);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * greater than or equal to fromKey.
+ * This method conforms to the {@link SortedSet#tailSet} interface.
+ *
+ * @param fromKey is the lower bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet tailSet(Object fromKey) {
+
+ return subSet(fromKey, true, null, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly greater than fromKey, optionally including fromKey.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param fromKey is the lower bound.
+ *
+ * @param fromInclusive is true to include fromKey.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet tailSet(Object fromKey, boolean fromInclusive) {
+
+ return subSet(fromKey, fromInclusive, null, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements range
+ * from fromKey, inclusive, to toKey, exclusive.
+ * This method conforms to the {@link SortedSet#subSet} interface.
+ *
+ * @param fromKey is the lower bound.
+ *
+ * @param toKey is the upper bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet subSet(Object fromKey, Object toKey) {
+
+ return subSet(fromKey, true, toKey, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly greater than fromKey and strictly less than toKey,
+ * optionally including fromKey and toKey.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param fromKey is the lower bound.
+ *
+ * @param fromInclusive is true to include fromKey.
+ *
+ * @param toKey is the upper bound.
+ *
+ * @param toInclusive is true to include toKey.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet subSet(Object fromKey, boolean fromInclusive,
+ Object toKey, boolean toInclusive) {
+
+ try {
+ return new StoredSortedKeySet(
+ view.subView(fromKey, fromInclusive, toKey, toInclusive, null));
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredSortedMap.java b/db/java/src/com/sleepycat/collections/StoredSortedMap.java
new file mode 100644
index 000000000..142e75ff4
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredSortedMap.java
@@ -0,0 +1,348 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredSortedMap.java,v 1.3 2004/09/22 18:01:03 bostic Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Comparator;
+import java.util.SortedMap;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.OperationStatus;
+
+/**
+ * A SortedMap view of a {@link Database}.
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * <p>In addition to the standard SortedMap methods, this class provides the
+ * following methods for stored sorted maps only. Note that the use of these
+ * methods is not compatible with the standard Java collections interface.</p>
+ * <ul>
+ * <li>{@link #duplicates(Object)}</li>
+ * <li>{@link #headMap(Object, boolean)}</li>
+ * <li>{@link #tailMap(Object, boolean)}</li>
+ * <li>{@link #subMap(Object, boolean, Object, boolean)}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredSortedMap extends StoredMap implements SortedMap {
+
+ /**
+ * Creates a sorted map view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param valueBinding is the binding used to translate between value
+ * buffers and value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredSortedMap(Database database, EntryBinding keyBinding,
+ EntryBinding valueBinding, boolean writeAllowed) {
+
+ super(new DataView(database, keyBinding, valueBinding, null,
+ writeAllowed, null));
+ }
+
+ /**
+ * Creates a sorted map view of a {@link Database} with a {@link
+ * PrimaryKeyAssigner}. Writing is allowed for the created map.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param valueBinding is the binding used to translate between value
+ * buffers and value objects.
+ *
+ * @param keyAssigner is used by the {@link #append} method to assign
+ * primary keys.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredSortedMap(Database database, EntryBinding keyBinding,
+ EntryBinding valueBinding,
+ PrimaryKeyAssigner keyAssigner) {
+
+ super(new DataView(database, keyBinding, valueBinding, null,
+ true, keyAssigner));
+ }
+
+ /**
+ * Creates a sorted map entity view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param valueEntityBinding is the binding used to translate between
+ * key/value buffers and entity value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredSortedMap(Database database, EntryBinding keyBinding,
+ EntityBinding valueEntityBinding,
+ boolean writeAllowed) {
+
+ super(new DataView(database, keyBinding, null, valueEntityBinding,
+ writeAllowed, null));
+ }
+
+ /**
+ * Creates a sorted map entity view of a {@link Database} with a {@link
+ * PrimaryKeyAssigner}. Writing is allowed for the created map.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param keyBinding is the binding used to translate between key buffers
+ * and key objects.
+ *
+ * @param valueEntityBinding is the binding used to translate between
+ * key/value buffers and entity value objects.
+ *
+ * @param keyAssigner is used by the {@link #append} method to assign
+ * primary keys.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredSortedMap(Database database, EntryBinding keyBinding,
+ EntityBinding valueEntityBinding,
+ PrimaryKeyAssigner keyAssigner) {
+
+ super(new DataView(database, keyBinding, null, valueEntityBinding,
+ true, keyAssigner));
+ }
+
+ StoredSortedMap(DataView mapView) {
+
+ super(mapView);
+ }
+
+ /**
+ * Returns null since comparators are not supported. The natural ordering
+ * of a stored collection is data byte order, whether the data classes
+ * implement the {@link java.lang.Comparable} interface or not.
+ * This method does not conform to the {@link SortedMap#comparator}
+ * interface.
+ *
+ * @return null.
+ */
+ public Comparator comparator() {
+
+ return null;
+ }
+
+ /**
+ * Returns the first (lowest) key currently in this sorted map.
+ * This method conforms to the {@link SortedMap#firstKey} interface.
+ *
+ * @return the first key.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object firstKey() {
+
+ return getFirstOrLastKey(true);
+ }
+
+ /**
+ * Returns the last (highest) element currently in this sorted map.
+ * This method conforms to the {@link SortedMap#lastKey} interface.
+ *
+ * @return the last key.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object lastKey() {
+
+ return getFirstOrLastKey(false);
+ }
+
+ private Object getFirstOrLastKey(boolean doGetFirst) {
+
+ DataCursor cursor = null;
+ try {
+ cursor = new DataCursor(view, false);
+ OperationStatus status;
+ if (doGetFirst) {
+ status = cursor.getFirst(false);
+ } else {
+ status = cursor.getLast(false);
+ }
+ return (status == OperationStatus.SUCCESS) ?
+ cursor.getCurrentKey() : null;
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ } finally {
+ closeCursor(cursor);
+ }
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose keys are
+ * strictly less than toKey.
+ * This method conforms to the {@link SortedMap#headMap} interface.
+ *
+ * @param toKey is the upper bound.
+ *
+ * @return the submap.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedMap headMap(Object toKey) {
+
+ return subMap(null, false, toKey, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted map whose elements are
+ * strictly less than toKey, optionally including toKey.
+ * This method does not exist in the standard {@link SortedMap} interface.
+ *
+ * @param toKey is the upper bound.
+ *
+ * @param toInclusive is true to include toKey.
+ *
+ * @return the submap.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedMap headMap(Object toKey, boolean toInclusive) {
+
+ return subMap(null, false, toKey, toInclusive);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted map whose elements are
+ * greater than or equal to fromKey.
+ * This method conforms to the {@link SortedMap#tailMap} interface.
+ *
+ * @param fromKey is the lower bound.
+ *
+ * @return the submap.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedMap tailMap(Object fromKey) {
+
+ return subMap(fromKey, true, null, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted map whose elements are
+ * strictly greater than fromKey, optionally including fromKey.
+ * This method does not exist in the standard {@link SortedMap} interface.
+ *
+ * @param fromKey is the lower bound.
+ *
+ * @param fromInclusive is true to include fromKey.
+ *
+ * @return the submap.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedMap tailMap(Object fromKey, boolean fromInclusive) {
+
+ return subMap(fromKey, fromInclusive, null, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted map whose elements range
+ * from fromKey, inclusive, to toKey, exclusive.
+ * This method conforms to the {@link SortedMap#subMap} interface.
+ *
+ * @param fromKey is the lower bound.
+ *
+ * @param toKey is the upper bound.
+ *
+ * @return the submap.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedMap subMap(Object fromKey, Object toKey) {
+
+ return subMap(fromKey, true, toKey, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted map whose elements are
+ * strictly greater than fromKey and strictly less than toKey,
+ * optionally including fromKey and toKey.
+ * This method does not exist in the standard {@link SortedMap} interface.
+ *
+ * @param fromKey is the lower bound.
+ *
+ * @param fromInclusive is true to include fromKey.
+ *
+ * @param toKey is the upper bound.
+ *
+ * @param toInclusive is true to include toKey.
+ *
+ * @return the submap.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedMap subMap(Object fromKey, boolean fromInclusive,
+ Object toKey, boolean toInclusive) {
+
+ try {
+ return new StoredSortedMap(
+ view.subView(fromKey, fromInclusive, toKey, toInclusive, null));
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredSortedValueSet.java b/db/java/src/com/sleepycat/collections/StoredSortedValueSet.java
new file mode 100644
index 000000000..96c534137
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredSortedValueSet.java
@@ -0,0 +1,255 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredSortedValueSet.java,v 1.2 2004/06/02 20:59:39 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Comparator;
+import java.util.SortedSet;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.db.Database;
+
+/**
+ * The SortedSet returned by Map.values() and which can also be constructed
+ * directly if a Map is not needed.
+ * Although this collection is a set it may contain duplicate values. Only if
+ * an entity value binding is used are all elements guaranteed to be unique.
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * <p>In addition to the standard SortedSet methods, this class provides the
+ * following methods for stored sorted value sets only. Note that the use of
+ * these methods is not compatible with the standard Java collections
+ * interface.</p>
+ * <ul>
+ * <li>{@link #headSet(Object, boolean)}</li>
+ * <li>{@link #tailSet(Object, boolean)}</li>
+ * <li>{@link #subSet(Object, boolean, Object, boolean)}</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredSortedValueSet extends StoredValueSet implements SortedSet {
+
+ /*
+ * No valueBinding ctor is possible since key cannot be derived.
+ */
+
+ /**
+ * Creates a sorted value set entity view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param valueEntityBinding is the binding used to translate between
+ * key/value buffers and entity value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public StoredSortedValueSet(Database database,
+ EntityBinding valueEntityBinding,
+ boolean writeAllowed) {
+
+ super(new DataView(database, null, null, valueEntityBinding,
+ writeAllowed, null));
+ checkKeyDerivation();
+ }
+
+ StoredSortedValueSet(DataView valueSetView) {
+
+ super(valueSetView);
+ checkKeyDerivation();
+ }
+
+ private void checkKeyDerivation() {
+
+ if (!view.canDeriveKeyFromValue()) {
+ throw new IllegalArgumentException("Cannot derive key from value");
+ }
+ }
+
+ /**
+ * Returns null since comparators are not supported. The natural ordering
+ * of a stored collection is data byte order, whether the data classes
+ * implement the {@link java.lang.Comparable} interface or not.
+ * This method does not conform to the {@link SortedSet#comparator}
+ * interface.
+ *
+ * @return null.
+ */
+ public Comparator comparator() {
+
+ return null;
+ }
+
+ /**
+ * Returns the first (lowest) element currently in this sorted set.
+ * This method conforms to the {@link SortedSet#first} interface.
+ *
+ * @return the first element.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object first() {
+
+ return getFirstOrLast(true);
+ }
+
+ /**
+ * Returns the last (highest) element currently in this sorted set.
+ * This method conforms to the {@link SortedSet#last} interface.
+ *
+ * @return the last element.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public Object last() {
+
+ return getFirstOrLast(false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly less than toValue.
+ * This method conforms to the {@link SortedSet#headSet} interface.
+ *
+ * @param toValue the upper bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet headSet(Object toValue) {
+
+ return subSet(null, false, toValue, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly less than toValue, optionally including toValue.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param toValue is the upper bound.
+ *
+ * @param toInclusive is true to include toValue.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet headSet(Object toValue, boolean toInclusive) {
+
+ return subSet(null, false, toValue, toInclusive);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * greater than or equal to fromValue.
+ * This method conforms to the {@link SortedSet#tailSet} interface.
+ *
+ * @param fromValue is the lower bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet tailSet(Object fromValue) {
+
+ return subSet(fromValue, true, null, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly greater than fromValue, optionally including fromValue.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param fromValue is the lower bound.
+ *
+ * @param fromInclusive is true to include fromValue.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet tailSet(Object fromValue, boolean fromInclusive) {
+
+ return subSet(fromValue, fromInclusive, null, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements range
+ * from fromValue, inclusive, to toValue, exclusive.
+ * This method conforms to the {@link SortedSet#subSet} interface.
+ *
+ * @param fromValue is the lower bound.
+ *
+ * @param toValue is the upper bound.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet subSet(Object fromValue, Object toValue) {
+
+ return subSet(fromValue, true, toValue, false);
+ }
+
+ /**
+ * Returns a view of the portion of this sorted set whose elements are
+ * strictly greater than fromValue and strictly less than toValue,
+ * optionally including fromValue and toValue.
+ * This method does not exist in the standard {@link SortedSet} interface.
+ *
+ * @param fromValue is the lower bound.
+ *
+ * @param fromInclusive is true to include fromValue.
+ *
+ * @param toValue is the upper bound.
+ *
+ * @param toInclusive is true to include toValue.
+ *
+ * @return the subset.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link
+ * com.sleepycat.db.DatabaseException} is thrown.
+ */
+ public SortedSet subSet(Object fromValue, boolean fromInclusive,
+ Object toValue, boolean toInclusive) {
+
+ try {
+ return new StoredSortedValueSet(
+ view.subView(fromValue, fromInclusive, toValue, toInclusive,
+ null));
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/StoredValueSet.java b/db/java/src/com/sleepycat/collections/StoredValueSet.java
new file mode 100644
index 000000000..a4d30bc87
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/StoredValueSet.java
@@ -0,0 +1,220 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: StoredValueSet.java,v 1.3 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import java.util.Set;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.OperationStatus;
+
+/**
+ * The Set returned by Map.values() and Map.duplicates(), and which can also be
+ * constructed directly if a Map is not needed.
+ * Although this collection is a set it may contain duplicate values. Only if
+ * an entity value binding is used are all elements guaranteed to be unique.
+ *
+ * <p><em>Note that this class does not conform to the standard Java
+ * collections interface in the following ways:</em></p>
+ * <ul>
+ * <li>The {@link #size} method always throws
+ * <code>UnsupportedOperationException</code> because, for performance reasons,
+ * databases do not maintain their total record count.</li>
+ * <li>All iterators must be explicitly closed using {@link
+ * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)}
+ * to release the underlying database cursor resources.</li>
+ * </ul>
+ *
+ * @author Mark Hayes
+ */
+public class StoredValueSet extends StoredCollection implements Set {
+
+ /*
+ * This class is also used internally for the set returned by duplicates().
+ */
+
+ private boolean isSingleKey;
+
+ /**
+ * Creates a value set view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param valueBinding is the binding used to translate between value
+ * buffers and value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public StoredValueSet(Database database,
+ EntryBinding valueBinding,
+ boolean writeAllowed) {
+
+ super(new DataView(database, null, valueBinding, null,
+ writeAllowed, null));
+ }
+
+ /**
+ * Creates a value set entity view of a {@link Database}.
+ *
+ * @param database is the Database underlying the new collection.
+ *
+ * @param valueEntityBinding is the binding used to translate between
+ * key/value buffers and entity value objects.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ *
+ * @throws IllegalArgumentException if formats are not consistently
+ * defined or a parameter is invalid.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public StoredValueSet(Database database,
+ EntityBinding valueEntityBinding,
+ boolean writeAllowed) {
+
+ super(new DataView(database, null, null, valueEntityBinding,
+ writeAllowed, null));
+ }
+
+ StoredValueSet(DataView valueSetView) {
+
+ super(valueSetView);
+ }
+
+ StoredValueSet(DataView valueSetView, boolean isSingleKey) {
+
+ super(valueSetView);
+ this.isSingleKey = isSingleKey;
+ }
+
+ /**
+ * Adds the specified entity to this set if it is not already present
+ * (optional operation).
+ * This method conforms to the {@link Set#add} interface.
+ *
+ * @param entity is the entity to be added.
+ *
+ * @return true if the entity was added, that is the key-value pair
+ * represented by the entity was not previously present in the collection.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only,
+ * if the collection is indexed, or if an entity binding is not used.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean add(Object entity) {
+
+ if (view.isSecondary()) {
+ throw new UnsupportedOperationException(
+ "add() not allowed with index");
+ } else if (isSingleKey) {
+ /* entity is actually just a value in this case */
+ if (!view.dupsAllowed) {
+ throw new UnsupportedOperationException("duplicates required");
+ }
+ DataCursor cursor = null;
+ boolean doAutoCommit = beginAutoCommit();
+ try {
+ cursor = new DataCursor(view, true);
+ cursor.useRangeKey();
+ OperationStatus status =
+ cursor.putNoDupData(null, entity, null, true);
+ closeCursor(cursor);
+ commitAutoCommit(doAutoCommit);
+ return (status == OperationStatus.SUCCESS);
+ } catch (Exception e) {
+ closeCursor(cursor);
+ throw handleException(e, doAutoCommit);
+ }
+ } else if (view.entityBinding == null) {
+ throw new UnsupportedOperationException(
+ "add() requires entity binding");
+ } else {
+ return add(null, entity);
+ }
+ }
+
+ /**
+ * Returns true if this set contains the specified element.
+ * This method conforms to the {@link java.util.Set#contains}
+ * interface.
+ *
+ * @param value the value to check.
+ *
+ * @return whether the set contains the given value.
+ */
+ public boolean contains(Object value) {
+
+ return containsValue(value);
+ }
+
+ /**
+ * Removes the specified value from this set if it is present (optional
+ * operation).
+ * If an entity binding is used, the key-value pair represented by the
+ * given entity is removed. If an entity binding is used, the first
+ * occurrence of a key-value pair with the given value is removed.
+ * This method conforms to the {@link Set#remove} interface.
+ *
+ * @throws UnsupportedOperationException if the collection is read-only.
+ *
+ * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is
+ * thrown.
+ */
+ public boolean remove(Object value) {
+
+ return removeValue(value);
+ }
+
+ // javadoc is inherited
+ public int size() {
+
+ if (!isSingleKey) {
+ return super.size();
+ }
+ DataCursor cursor = null;
+ try {
+ cursor = new DataCursor(view, false);
+ OperationStatus status = cursor.getFirst(false);
+ if (status == OperationStatus.SUCCESS) {
+ return cursor.count();
+ } else {
+ return 0;
+ }
+ } catch (Exception e) {
+ throw StoredContainer.convertException(e);
+ } finally {
+ closeCursor(cursor);
+ }
+ }
+
+ Object makeIteratorData(StoredIterator iterator, DataCursor cursor)
+ throws DatabaseException {
+
+ return cursor.getCurrentValue();
+ }
+
+ boolean hasValues() {
+
+ return true;
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/TransactionRunner.java b/db/java/src/com/sleepycat/collections/TransactionRunner.java
new file mode 100644
index 000000000..270d265fb
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/TransactionRunner.java
@@ -0,0 +1,221 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TransactionRunner.java,v 1.2 2004/09/22 18:01:03 bostic Exp $
+ */
+
+package com.sleepycat.collections;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.db.TransactionConfig;
+import com.sleepycat.util.ExceptionUnwrapper;
+
+/**
+ * Starts a transaction, calls {@link TransactionWorker#doWork}, and handles
+ * transaction retry and exceptions.
+ *
+ * @author Mark Hayes
+ */
+public class TransactionRunner {
+
+ /** The default maximum number of retries. */
+ public static final int DEFAULT_MAX_RETRIES = 10;
+
+ private Environment env;
+ private CurrentTransaction currentTxn;
+ private int maxRetries;
+ private TransactionConfig config;
+ private boolean allowNestedTxn;
+
+ /**
+ * Creates a transaction runner for a given Berkeley DB environment.
+ * The default maximum number of retries ({@link #DEFAULT_MAX_RETRIES}) and
+ * a null (default) {@link TransactionConfig} will be used.
+ *
+ * @param env is the environment for running transactions.
+ */
+ public TransactionRunner(Environment env) {
+
+ this(env, DEFAULT_MAX_RETRIES, null);
+ }
+
+ /**
+ * Creates a transaction runner for a given Berkeley DB environment and
+ * with a given number of maximum retries.
+ *
+ * @param env is the environment for running transactions.
+ *
+ * @param maxRetries is the maximum number of retries that will be
+ * performed when deadlocks are detected.
+ *
+ * @param config the transaction configuration used for calling
+ * {@link Environment#beginTransaction}, or null to use the default
+ * configuration. The configuration object is not cloned, and
+ * any modifications to it will impact subsequent transactions.
+ */
+ public TransactionRunner(Environment env, int maxRetries,
+ TransactionConfig config) {
+
+ this.env = env;
+ this.currentTxn = CurrentTransaction.getInstance(env);
+ this.maxRetries = maxRetries;
+ this.config = config;
+ }
+
+ /**
+ * Returns the maximum number of retries that will be performed when
+ * deadlocks are detected.
+ */
+ public int getMaxRetries() {
+
+ return maxRetries;
+ }
+
+ /**
+ * Changes the maximum number of retries that will be performed when
+ * deadlocks are detected.
+ * Calling this method does not impact transactions already running.
+ */
+ public void setMaxRetries(int maxRetries) {
+
+ this.maxRetries = maxRetries;
+ }
+
+ /**
+ * Returns whether nested transactions will be created if
+ * <code>run()</code> is called when a transaction is already active for
+ * the current thread.
+ * By default this property is false.
+ */
+ public boolean getAllowNestedTransactions() {
+
+ return allowNestedTxn;
+ }
+
+ /**
+ * Changes whether nested transactions will be created if
+ * <code>run()</code> is called when a transaction is already active for
+ * the current thread.
+ * Calling this method does not impact transactions already running.
+ */
+ public void setAllowNestedTransactions(boolean allowNestedTxn) {
+
+ if (allowNestedTxn && !DbCompat.NESTED_TRANSACTIONS) {
+ throw new UnsupportedOperationException(
+ "Nested transactions are not supported.");
+ }
+ this.allowNestedTxn = allowNestedTxn;
+ }
+
+ /**
+ * Returns the transaction configuration used for calling
+ * {@link Environment#beginTransaction}.
+ *
+ * <p>If this property is null, the default configuration is used. The
+ * configuration object is not cloned, and any modifications to it will
+ * impact subsequent transactions.</p>
+ *
+ * @return the transaction configuration.
+ */
+ public TransactionConfig getTransactionConfig() {
+
+ return config;
+ }
+
+ /**
+ * Changes the transaction configuration used for calling
+ * {@link Environment#beginTransaction}.
+ *
+ * <p>If this property is null, the default configuration is used. The
+ * configuration object is not cloned, and any modifications to it will
+ * impact subsequent transactions.</p>
+ *
+ * @param config the transaction configuration.
+ */
+ public void setTransactionConfig(TransactionConfig config) {
+
+ this.config = config;
+ }
+
+ /**
+ * Calls the {@link TransactionWorker#doWork} method and, for transactional
+ * environments, begins and ends a transaction. If the environment given
+ * is non-transactional, a transaction will not be used but the doWork()
+ * method will still be called.
+ *
+ * <p> In a transactional environment, a new transaction is started before
+ * calling doWork(). This will start a nested transaction if one is
+ * already active. If DeadlockException is thrown by doWork(), the
+ * transaction will be aborted and the process will be repeated up to the
+ * maximum number of retries specified. If another exception is thrown by
+ * doWork() or the maximum number of retries has occurred, the transaction
+ * will be aborted and the exception will be rethrown by this method. If
+ * no exception is thrown by doWork(), the transaction will be committed.
+ * This method will not attempt to commit or abort a transaction if it has
+ * already been committed or aborted by doWork(). </p>
+ *
+ * @throws DeadlockException when it is thrown by doWork() and the
+ * maximum number of retries has occurred. The transaction will have been
+ * aborted by this method.
+ *
+ * @throws Exception when any other exception is thrown by doWork(). The
+ * exception will first be unwrapped by calling {@link
+ * ExceptionUnwrapper#unwrap}. The transaction will have been aborted by
+ * this method.
+ */
+ public void run(TransactionWorker worker)
+ throws DatabaseException, Exception {
+
+ if (currentTxn != null &&
+ (allowNestedTxn || currentTxn.getTransaction() == null)) {
+ /*
+ * Transactional and (not nested or nested txns allowed).
+ */
+ for (int i = 0;; i += 1) {
+ Transaction txn = null;
+ try {
+ txn = currentTxn.beginTransaction(config);
+ worker.doWork();
+ if (txn != null && txn == currentTxn.getTransaction()) {
+ currentTxn.commitTransaction();
+ }
+ return;
+ } catch (Exception e) {
+ e = ExceptionUnwrapper.unwrap(e);
+ if (txn != null && txn == currentTxn.getTransaction()) {
+ try {
+ currentTxn.abortTransaction();
+ } catch (Exception e2) {
+ /*
+ * XXX We should really throw a 3rd exception that
+ * wraps both e and e2, to give the user a complete
+ * set of error information.
+ */
+ e2.printStackTrace();
+ throw e;
+ }
+ }
+ if (i >= maxRetries || !(e instanceof DeadlockException)) {
+ throw e;
+ }
+ }
+ }
+ } else {
+ /*
+ * Non-transactional or (nested and no nested txns allowed).
+ */
+ try {
+ worker.doWork();
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ }
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/collections/TransactionWorker.java b/db/java/src/com/sleepycat/collections/TransactionWorker.java
new file mode 100644
index 000000000..eb69c7095
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/TransactionWorker.java
@@ -0,0 +1,28 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TransactionWorker.java,v 1.1 2004/04/09 16:34:10 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+/**
+ * The interface implemented to perform the work within a transaction.
+ * To run a transaction, an instance of this interface is passed to the
+ * {@link TransactionRunner#run} method.
+ *
+ * @author Mark Hayes
+ */
+public interface TransactionWorker {
+
+ /**
+ * Perform the work for a single transaction.
+ *
+ * @see TransactionRunner#run
+ */
+ void doWork()
+ throws Exception;
+}
diff --git a/db/java/src/com/sleepycat/collections/TupleSerialFactory.java b/db/java/src/com/sleepycat/collections/TupleSerialFactory.java
new file mode 100644
index 000000000..b8382a1a8
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/TupleSerialFactory.java
@@ -0,0 +1,135 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TupleSerialFactory.java,v 1.1 2004/04/09 16:34:10 mark Exp $
+ */
+
+package com.sleepycat.collections;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialMarshalledBinding;
+import com.sleepycat.bind.serial.TupleSerialMarshalledKeyCreator;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleMarshalledBinding;
+import com.sleepycat.db.Database;
+
+/**
+ * Creates stored collections having tuple keys and serialized entity values.
+ * The entity classes must implement the java.io.Serializable and
+ * MarshalledTupleKeyEntity interfaces. The key classes must either implement
+ * the MarshalledTupleEntry interface or be one of the Java primitive type
+ * classes. Underlying binding objects are created automatically.
+ *
+ * @author Mark Hayes
+ */
+public class TupleSerialFactory {
+
+ private ClassCatalog catalog;
+
+ /**
+ * Creates a tuple-serial factory for given environment and class catalog.
+ */
+ public TupleSerialFactory(ClassCatalog catalog) {
+
+ this.catalog = catalog;
+ }
+
+ /**
+ * Returns the class catalog associated with this factory.
+ */
+ public final ClassCatalog getCatalog() {
+
+ return catalog;
+ }
+
+ /**
+ * Creates a map from a previously opened Database object.
+ *
+ * @param db the previously opened Database object.
+ *
+ * @param keyClass is the class used for map keys. It must implement the
+ * {@link com.sleepycat.bind.tuple.MarshalledTupleEntry} interface or be
+ * one of the Java primitive type classes.
+ *
+ * @param valueBaseClass the base class of the entity values for this
+ * store. It must implement the {@link
+ * com.sleepycat.bind.tuple.MarshalledTupleKeyEntity} interface.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ */
+ public StoredMap newMap(Database db, Class keyClass, Class valueBaseClass,
+ boolean writeAllowed) {
+
+ return new StoredMap(db,
+ getKeyBinding(keyClass),
+ getEntityBinding(valueBaseClass),
+ writeAllowed);
+ }
+
+ /**
+ * Creates a sorted map from a previously opened Database object.
+ *
+ * @param db the previously opened Database object.
+ *
+ * @param keyClass is the class used for map keys. It must implement the
+ * {@link com.sleepycat.bind.tuple.MarshalledTupleEntry} interface or be
+ * one of the Java primitive type classes.
+ *
+ * @param valueBaseClass the base class of the entity values for this
+ * store. It must implement the {@link
+ * com.sleepycat.bind.tuple.MarshalledTupleKeyEntity} interface.
+ *
+ * @param writeAllowed is true to create a read-write collection or false
+ * to create a read-only collection.
+ */
+ public StoredSortedMap newSortedMap(Database db, Class keyClass,
+ Class valueBaseClass,
+ boolean writeAllowed) {
+
+ return new StoredSortedMap(db,
+ getKeyBinding(keyClass),
+ getEntityBinding(valueBaseClass),
+ writeAllowed);
+ }
+
+ /**
+ * Creates a <code>SecondaryKeyCreator</code> object for use in configuring
+ * a <code>SecondaryDatabase</code>. The returned object implements
+ * the {@link com.sleepycat.db.SecondaryKeyCreator} interface.
+ *
+ * @param valueBaseClass the base class of the entity values for this
+ * store. It must implement the {@link
+ * com.sleepycat.bind.tuple.MarshalledTupleKeyEntity} interface.
+ *
+ * @param keyName is the key name passed to the {@link
+ * com.sleepycat.bind.tuple.MarshalledTupleKeyEntity#marshalSecondaryKey}
+ * method to identify the secondary key.
+ */
+ public TupleSerialMarshalledKeyCreator getKeyCreator(Class valueBaseClass,
+ String keyName) {
+
+ return new TupleSerialMarshalledKeyCreator(
+ getEntityBinding(valueBaseClass),
+ keyName);
+ }
+
+ private TupleSerialMarshalledBinding getEntityBinding(Class baseClass) {
+
+ return new TupleSerialMarshalledBinding(catalog, baseClass);
+ }
+
+ private EntryBinding getKeyBinding(Class keyClass) {
+
+ EntryBinding binding = TupleBinding.getPrimitiveBinding(keyClass);
+ if (binding == null) {
+ binding = new TupleMarshalledBinding(keyClass);
+ }
+ return binding;
+ }
+}
+
diff --git a/db/java/src/com/sleepycat/collections/package.html b/db/java/src/com/sleepycat/collections/package.html
new file mode 100644
index 000000000..865f36b0f
--- /dev/null
+++ b/db/java/src/com/sleepycat/collections/package.html
@@ -0,0 +1,21 @@
+<!-- $Id: package.html,v 1.1 2004/08/02 18:52:06 mjc Exp $ -->
+<html>
+<head>
+<!--
+
+ See the file LICENSE for redistribution information.
+
+ Copyright (c) 2002-2004
+ Sleepycat Software. All rights reserved.
+
+ $Id: package.html,v 1.1 2004/08/02 18:52:06 mjc Exp $
+
+-->
+</head>
+<body>
+Data access based on the standard Java collections API<br>
+<a href="{@docRoot}/%2e%2e/ref/bdb/cs_bdb_collection%2ehtml">[reference guide]</a>.
+<p>
+Examples can be found in je/examples/com/sleepycat/examples/collections. Build and run directions are in the installation notes.
+</body>
+</html>
diff --git a/db/java/src/com/sleepycat/compat/DbCompat.java b/db/java/src/com/sleepycat/compat/DbCompat.java
new file mode 100644
index 000000000..84e68bace
--- /dev/null
+++ b/db/java/src/com/sleepycat/compat/DbCompat.java
@@ -0,0 +1,255 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DbCompat.java,v 1.5 2004/09/22 18:01:03 bostic Exp $
+ */
+
+package com.sleepycat.compat;
+
+import java.io.FileNotFoundException;
+import java.util.Comparator;
+
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.CursorConfig;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.LockDetectMode;
+import com.sleepycat.db.LockMode;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryCursor;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.db.Transaction;
+
+/**
+ * A minimal set of DB-JE compatibility methods for internal use only.
+ * Two versions are maintained in parallel in the DB and JE source trees.
+ * Used by the collections package.
+ */
+public class DbCompat {
+
+ /* Capabilities */
+
+ public static final boolean CDB = true;
+ public static final boolean JOIN = true;
+ public static final boolean NESTED_TRANSACTIONS = true;
+ public static final boolean INSERTION_ORDERED_DUPLICATES = true;
+ public static final boolean SEPARATE_DATABASE_FILES = true;
+ public static final boolean MEMORY_SUBSYSTEM = true;
+ public static final boolean LOCK_SUBSYSTEM = true;
+ public static final boolean HASH_METHOD = true;
+ public static final boolean RECNO_METHOD = true;
+ public static final boolean QUEUE_METHOD = true;
+ public static final boolean BTREE_RECNUM_METHOD = true;
+ public static final boolean OPTIONAL_DIRTY_READ = true;
+ public static final boolean SECONDARIES = true;
+
+ /* Methods used by the collections package. */
+
+ public static boolean getInitializeLocking(EnvironmentConfig config) {
+ return config.getInitializeLocking();
+ }
+
+ public static boolean getInitializeCDB(EnvironmentConfig config) {
+ return config.getInitializeCDB();
+ }
+
+ public static boolean isTypeBtree(DatabaseConfig dbConfig) {
+ return dbConfig.getType() == DatabaseType.BTREE;
+ }
+
+ public static boolean isTypeHash(DatabaseConfig dbConfig) {
+ return dbConfig.getType() == DatabaseType.HASH;
+ }
+
+ public static boolean isTypeQueue(DatabaseConfig dbConfig) {
+ return dbConfig.getType() == DatabaseType.QUEUE;
+ }
+
+ public static boolean isTypeRecno(DatabaseConfig dbConfig) {
+ return dbConfig.getType() == DatabaseType.RECNO;
+ }
+
+ public static boolean getBtreeRecordNumbers(DatabaseConfig dbConfig) {
+ return dbConfig.getBtreeRecordNumbers();
+ }
+
+ public static boolean getDirtyRead(DatabaseConfig dbConfig) {
+ return dbConfig.getDirtyRead();
+ }
+
+ public static boolean getRenumbering(DatabaseConfig dbConfig) {
+ return dbConfig.getRenumbering();
+ }
+
+ public static boolean getSortedDuplicates(DatabaseConfig dbConfig) {
+ return dbConfig.getSortedDuplicates();
+ }
+
+ public static boolean getUnsortedDuplicates(DatabaseConfig dbConfig) {
+ return dbConfig.getUnsortedDuplicates();
+ }
+
+ public static void setWriteCursor(CursorConfig config, boolean val) {
+ config.setWriteCursor(val);
+ }
+
+ public static void setRecordNumber(DatabaseEntry entry, int recNum) {
+ entry.setRecordNumber(recNum);
+ }
+
+ public static int getRecordNumber(DatabaseEntry entry) {
+ return entry.getRecordNumber();
+ }
+
+ public static String getDatabaseFile(Database db)
+ throws DatabaseException {
+ return db.getDatabaseFile();
+ }
+
+ public static OperationStatus getCurrentRecordNumber(Cursor cursor,
+ DatabaseEntry key,
+ LockMode lockMode)
+ throws DatabaseException {
+ return cursor.getRecordNumber(key, lockMode);
+ }
+
+ public static OperationStatus getSearchRecordNumber(Cursor cursor,
+ DatabaseEntry key,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+ return cursor.getSearchRecordNumber(key, data, lockMode);
+ }
+
+ public static OperationStatus getSearchRecordNumber(SecondaryCursor cursor,
+ DatabaseEntry key,
+ DatabaseEntry pKey,
+ DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+ return cursor.getSearchRecordNumber(key, pKey, data, lockMode);
+ }
+
+ public static OperationStatus putAfter(Cursor cursor, DatabaseEntry data)
+ throws DatabaseException {
+ return cursor.putAfter(data);
+ }
+
+ public static OperationStatus putBefore(Cursor cursor, DatabaseEntry data)
+ throws DatabaseException {
+ return cursor.putBefore(data);
+ }
+
+ public static OperationStatus append(Database db,
+ Transaction txn,
+ DatabaseEntry key,
+ DatabaseEntry data)
+ throws DatabaseException {
+ return db.append(txn, key, data);
+ }
+
+ /* Methods used by the collections tests. */
+
+ public static void setInitializeCache(EnvironmentConfig config,
+ boolean val) {
+ config.setInitializeCache(val);
+ }
+
+ public static void setInitializeLocking(EnvironmentConfig config,
+ boolean val) {
+ config.setInitializeLocking(val);
+ }
+
+ public static void setInitializeCDB(EnvironmentConfig config,
+ boolean val) {
+ config.setInitializeCDB(val);
+ }
+
+ public static void setLockDetectModeOldest(EnvironmentConfig config) {
+
+ config.setLockDetectMode(LockDetectMode.OLDEST);
+ }
+
+ public static void setBtreeComparator(DatabaseConfig dbConfig,
+ Comparator comparator) {
+ dbConfig.setBtreeComparator(comparator);
+ }
+
+ public static void setTypeBtree(DatabaseConfig dbConfig) {
+ dbConfig.setType(DatabaseType.BTREE);
+ }
+
+ public static void setTypeHash(DatabaseConfig dbConfig) {
+ dbConfig.setType(DatabaseType.HASH);
+ }
+
+ public static void setTypeRecno(DatabaseConfig dbConfig) {
+ dbConfig.setType(DatabaseType.RECNO);
+ }
+
+ public static void setTypeQueue(DatabaseConfig dbConfig) {
+ dbConfig.setType(DatabaseType.QUEUE);
+ }
+
+ public static void setBtreeRecordNumbers(DatabaseConfig dbConfig,
+ boolean val) {
+ dbConfig.setBtreeRecordNumbers(val);
+ }
+
+ public static void setDirtyRead(DatabaseConfig dbConfig,
+ boolean val) {
+ dbConfig.setDirtyRead(val);
+ }
+
+ public static void setRenumbering(DatabaseConfig dbConfig,
+ boolean val) {
+ dbConfig.setRenumbering(val);
+ }
+
+ public static void setSortedDuplicates(DatabaseConfig dbConfig,
+ boolean val) {
+ dbConfig.setSortedDuplicates(val);
+ }
+
+ public static void setUnsortedDuplicates(DatabaseConfig dbConfig,
+ boolean val) {
+ dbConfig.setUnsortedDuplicates(val);
+ }
+
+ public static void setRecordLength(DatabaseConfig dbConfig, int val) {
+ dbConfig.setRecordLength(val);
+ }
+
+ public static void setRecordPad(DatabaseConfig dbConfig, int val) {
+ dbConfig.setRecordPad(val);
+ }
+
+ public static Database openDatabase(Environment env,
+ Transaction txn,
+ String file,
+ String name,
+ DatabaseConfig config)
+ throws DatabaseException, FileNotFoundException {
+ return env.openDatabase(txn, file, name, config);
+ }
+
+ public static SecondaryDatabase
+ openSecondaryDatabase(Environment env,
+ Transaction txn,
+ String file,
+ String name,
+ Database primary,
+ SecondaryConfig config)
+ throws DatabaseException, FileNotFoundException {
+ return env.openSecondaryDatabase(txn, file, name, primary, config);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/BtreePrefixCalculator.java b/db/java/src/com/sleepycat/db/BtreePrefixCalculator.java
new file mode 100644
index 000000000..ca86f0a68
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/BtreePrefixCalculator.java
@@ -0,0 +1,14 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: BtreePrefixCalculator.java,v 1.1 2004/04/06 20:43:36 mjc Exp $
+ */
+
+package com.sleepycat.db;
+
+public interface BtreePrefixCalculator {
+ int prefix(Database db, DatabaseEntry dbt1, DatabaseEntry dbt2);
+}
diff --git a/db/java/src/com/sleepycat/db/BtreeStats.java b/db/java/src/com/sleepycat/db/BtreeStats.java
new file mode 100644
index 000000000..50c8f9d7a
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/BtreeStats.java
@@ -0,0 +1,146 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class BtreeStats extends DatabaseStats {
+ // no public constructor
+ protected BtreeStats() {}
+
+ private int bt_magic;
+ public int getMagic() {
+ return bt_magic;
+ }
+
+ private int bt_version;
+ public int getVersion() {
+ return bt_version;
+ }
+
+ private int bt_metaflags;
+ public int getMetaFlags() {
+ return bt_metaflags;
+ }
+
+ private int bt_nkeys;
+ public int getNumKeys() {
+ return bt_nkeys;
+ }
+
+ private int bt_ndata;
+ public int getNumData() {
+ return bt_ndata;
+ }
+
+ private int bt_pagesize;
+ public int getPageSize() {
+ return bt_pagesize;
+ }
+
+ private int bt_maxkey;
+ public int getMaxKey() {
+ return bt_maxkey;
+ }
+
+ private int bt_minkey;
+ public int getMinKey() {
+ return bt_minkey;
+ }
+
+ private int bt_re_len;
+ public int getReLen() {
+ return bt_re_len;
+ }
+
+ private int bt_re_pad;
+ public int getRePad() {
+ return bt_re_pad;
+ }
+
+ private int bt_levels;
+ public int getLevels() {
+ return bt_levels;
+ }
+
+ private int bt_int_pg;
+ public int getIntPages() {
+ return bt_int_pg;
+ }
+
+ private int bt_leaf_pg;
+ public int getLeafPages() {
+ return bt_leaf_pg;
+ }
+
+ private int bt_dup_pg;
+ public int getDupPages() {
+ return bt_dup_pg;
+ }
+
+ private int bt_over_pg;
+ public int getOverPages() {
+ return bt_over_pg;
+ }
+
+ private int bt_empty_pg;
+ public int getEmptyPages() {
+ return bt_empty_pg;
+ }
+
+ private int bt_free;
+ public int getFree() {
+ return bt_free;
+ }
+
+ private int bt_int_pgfree;
+ public int getIntPagesFree() {
+ return bt_int_pgfree;
+ }
+
+ private int bt_leaf_pgfree;
+ public int getLeafPagesFree() {
+ return bt_leaf_pgfree;
+ }
+
+ private int bt_dup_pgfree;
+ public int getDupPagesFree() {
+ return bt_dup_pgfree;
+ }
+
+ private int bt_over_pgfree;
+ public int getOverPagesFree() {
+ return bt_over_pgfree;
+ }
+
+ public String toString() {
+ return "BtreeStats:"
+ + "\n bt_magic=" + bt_magic
+ + "\n bt_version=" + bt_version
+ + "\n bt_metaflags=" + bt_metaflags
+ + "\n bt_nkeys=" + bt_nkeys
+ + "\n bt_ndata=" + bt_ndata
+ + "\n bt_pagesize=" + bt_pagesize
+ + "\n bt_maxkey=" + bt_maxkey
+ + "\n bt_minkey=" + bt_minkey
+ + "\n bt_re_len=" + bt_re_len
+ + "\n bt_re_pad=" + bt_re_pad
+ + "\n bt_levels=" + bt_levels
+ + "\n bt_int_pg=" + bt_int_pg
+ + "\n bt_leaf_pg=" + bt_leaf_pg
+ + "\n bt_dup_pg=" + bt_dup_pg
+ + "\n bt_over_pg=" + bt_over_pg
+ + "\n bt_empty_pg=" + bt_empty_pg
+ + "\n bt_free=" + bt_free
+ + "\n bt_int_pgfree=" + bt_int_pgfree
+ + "\n bt_leaf_pgfree=" + bt_leaf_pgfree
+ + "\n bt_dup_pgfree=" + bt_dup_pgfree
+ + "\n bt_over_pgfree=" + bt_over_pgfree
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/CacheFile.java b/db/java/src/com/sleepycat/db/CacheFile.java
new file mode 100644
index 000000000..2fa44a19c
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/CacheFile.java
@@ -0,0 +1,70 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: CacheFile.java,v 1.3 2004/09/23 17:56:39 mjc Exp $
+ */
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbMpoolFile;
+
+public class CacheFile {
+ private DbMpoolFile mpf;
+
+ /* package */
+ CacheFile(final DbMpoolFile mpf) {
+ this.mpf = mpf;
+ }
+
+ public CacheFilePriority getPriority()
+ throws DatabaseException {
+
+ return CacheFilePriority.fromFlag(mpf.get_priority());
+ }
+
+ public void setPriority(final CacheFilePriority priority)
+ throws DatabaseException {
+
+ mpf.set_priority(priority.getFlag());
+ }
+
+ public long getMaximumSize()
+ throws DatabaseException {
+
+ return mpf.get_maxsize();
+ }
+
+ public void setMaximumSize(final long bytes)
+ throws DatabaseException {
+
+ mpf.set_maxsize(bytes);
+ }
+
+ public boolean getNoFile()
+ throws DatabaseException {
+
+ return (mpf.get_flags() & DbConstants.DB_MPOOL_NOFILE) != 0;
+ }
+
+ public void setNoFile(final boolean onoff)
+ throws DatabaseException {
+
+ mpf.set_flags(DbConstants.DB_MPOOL_NOFILE, onoff);
+ }
+
+ public boolean getUnlink()
+ throws DatabaseException {
+
+ return (mpf.get_flags() & DbConstants.DB_MPOOL_UNLINK) != 0;
+ }
+
+ public void setUnlink(boolean onoff)
+ throws DatabaseException {
+
+ mpf.set_flags(DbConstants.DB_MPOOL_UNLINK, onoff);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/CacheFilePriority.java b/db/java/src/com/sleepycat/db/CacheFilePriority.java
new file mode 100644
index 000000000..89f08c8f2
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/CacheFilePriority.java
@@ -0,0 +1,61 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: CacheFilePriority.java,v 1.2 2004/04/21 01:09:09 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public final class CacheFilePriority {
+ public static final CacheFilePriority DEFAULT =
+ new CacheFilePriority("DEFAULT", DbConstants.DB_PRIORITY_DEFAULT);
+ public static final CacheFilePriority HIGH =
+ new CacheFilePriority("HIGH", DbConstants.DB_PRIORITY_HIGH);
+ public static final CacheFilePriority LOW =
+ new CacheFilePriority("LOW", DbConstants.DB_PRIORITY_LOW);
+ public static final CacheFilePriority VERY_HIGH =
+ new CacheFilePriority("VERY_HIGH", DbConstants.DB_PRIORITY_VERY_HIGH);
+ public static final CacheFilePriority VERY_LOW =
+ new CacheFilePriority("VERY_LOW", DbConstants.DB_PRIORITY_VERY_LOW);
+
+ /* package */
+ static CacheFilePriority fromFlag(int flag) {
+ switch (flag) {
+ case DbConstants.DB_PRIORITY_DEFAULT:
+ return DEFAULT;
+ case DbConstants.DB_PRIORITY_HIGH:
+ return HIGH;
+ case DbConstants.DB_PRIORITY_LOW:
+ return LOW;
+ case DbConstants.DB_PRIORITY_VERY_HIGH:
+ return VERY_HIGH;
+ case DbConstants.DB_PRIORITY_VERY_LOW:
+ return VERY_LOW;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown cache priority: " + flag);
+ }
+ }
+
+ private final String priorityName;
+ private final int flag;
+
+ private CacheFilePriority(final String priorityName, final int flag) {
+ this.priorityName = priorityName;
+ this.flag = flag;
+ }
+
+ public String toString() {
+ return "CacheFilePriority." + priorityName;
+ }
+
+ /* package */
+ int getFlag() {
+ return flag;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/CacheFileStats.java b/db/java/src/com/sleepycat/db/CacheFileStats.java
new file mode 100644
index 000000000..7b864ed4e
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/CacheFileStats.java
@@ -0,0 +1,68 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class CacheFileStats {
+ // no public constructor
+ protected CacheFileStats() {}
+
+ private String file_name;
+ public String getFileName() {
+ return file_name;
+ }
+
+ private int st_pagesize;
+ public int getPageSize() {
+ return st_pagesize;
+ }
+
+ private int st_map;
+ public int getMap() {
+ return st_map;
+ }
+
+ private int st_cache_hit;
+ public int getCacheHit() {
+ return st_cache_hit;
+ }
+
+ private int st_cache_miss;
+ public int getCacheMiss() {
+ return st_cache_miss;
+ }
+
+ private int st_page_create;
+ public int getPageCreate() {
+ return st_page_create;
+ }
+
+ private int st_page_in;
+ public int getPageIn() {
+ return st_page_in;
+ }
+
+ private int st_page_out;
+ public int getPageOut() {
+ return st_page_out;
+ }
+
+ public String toString() {
+ return "CacheFileStats:"
+ + "\n file_name=" + file_name
+ + "\n st_pagesize=" + st_pagesize
+ + "\n st_map=" + st_map
+ + "\n st_cache_hit=" + st_cache_hit
+ + "\n st_cache_miss=" + st_cache_miss
+ + "\n st_page_create=" + st_page_create
+ + "\n st_page_in=" + st_page_in
+ + "\n st_page_out=" + st_page_out
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/CacheStats.java b/db/java/src/com/sleepycat/db/CacheStats.java
new file mode 100644
index 000000000..b9e16c96a
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/CacheStats.java
@@ -0,0 +1,224 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class CacheStats {
+ // no public constructor
+ protected CacheStats() {}
+
+ private int st_gbytes;
+ public int getGbytes() {
+ return st_gbytes;
+ }
+
+ private int st_bytes;
+ public int getBytes() {
+ return st_bytes;
+ }
+
+ private int st_ncache;
+ public int getNumCache() {
+ return st_ncache;
+ }
+
+ private int st_regsize;
+ public int getRegSize() {
+ return st_regsize;
+ }
+
+ private int st_mmapsize;
+ public int getMmapSize() {
+ return st_mmapsize;
+ }
+
+ private int st_maxopenfd;
+ public int getMaxOpenfd() {
+ return st_maxopenfd;
+ }
+
+ private int st_maxwrite;
+ public int getMaxWrite() {
+ return st_maxwrite;
+ }
+
+ private int st_maxwrite_sleep;
+ public int getMaxWriteSleep() {
+ return st_maxwrite_sleep;
+ }
+
+ private int st_map;
+ public int getMap() {
+ return st_map;
+ }
+
+ private int st_cache_hit;
+ public int getCacheHit() {
+ return st_cache_hit;
+ }
+
+ private int st_cache_miss;
+ public int getCacheMiss() {
+ return st_cache_miss;
+ }
+
+ private int st_page_create;
+ public int getPageCreate() {
+ return st_page_create;
+ }
+
+ private int st_page_in;
+ public int getPageIn() {
+ return st_page_in;
+ }
+
+ private int st_page_out;
+ public int getPageOut() {
+ return st_page_out;
+ }
+
+ private int st_ro_evict;
+ public int getRoEvict() {
+ return st_ro_evict;
+ }
+
+ private int st_rw_evict;
+ public int getRwEvict() {
+ return st_rw_evict;
+ }
+
+ private int st_page_trickle;
+ public int getPageTrickle() {
+ return st_page_trickle;
+ }
+
+ private int st_pages;
+ public int getPages() {
+ return st_pages;
+ }
+
+ private int st_page_clean;
+ public int getPageClean() {
+ return st_page_clean;
+ }
+
+ private int st_page_dirty;
+ public int getPageDirty() {
+ return st_page_dirty;
+ }
+
+ private int st_hash_buckets;
+ public int getHashBuckets() {
+ return st_hash_buckets;
+ }
+
+ private int st_hash_searches;
+ public int getHashSearches() {
+ return st_hash_searches;
+ }
+
+ private int st_hash_longest;
+ public int getHashLongest() {
+ return st_hash_longest;
+ }
+
+ private int st_hash_examined;
+ public int getHashExamined() {
+ return st_hash_examined;
+ }
+
+ private int st_hash_nowait;
+ public int getHashNowait() {
+ return st_hash_nowait;
+ }
+
+ private int st_hash_wait;
+ public int getHashWait() {
+ return st_hash_wait;
+ }
+
+ private int st_hash_max_wait;
+ public int getHashMaxWait() {
+ return st_hash_max_wait;
+ }
+
+ private int st_region_nowait;
+ public int getRegionNowait() {
+ return st_region_nowait;
+ }
+
+ private int st_region_wait;
+ public int getRegionWait() {
+ return st_region_wait;
+ }
+
+ private int st_alloc;
+ public int getAlloc() {
+ return st_alloc;
+ }
+
+ private int st_alloc_buckets;
+ public int getAllocBuckets() {
+ return st_alloc_buckets;
+ }
+
+ private int st_alloc_max_buckets;
+ public int getAllocMaxBuckets() {
+ return st_alloc_max_buckets;
+ }
+
+ private int st_alloc_pages;
+ public int getAllocPages() {
+ return st_alloc_pages;
+ }
+
+ private int st_alloc_max_pages;
+ public int getAllocMaxPages() {
+ return st_alloc_max_pages;
+ }
+
+ public String toString() {
+ return "CacheStats:"
+ + "\n st_gbytes=" + st_gbytes
+ + "\n st_bytes=" + st_bytes
+ + "\n st_ncache=" + st_ncache
+ + "\n st_regsize=" + st_regsize
+ + "\n st_mmapsize=" + st_mmapsize
+ + "\n st_maxopenfd=" + st_maxopenfd
+ + "\n st_maxwrite=" + st_maxwrite
+ + "\n st_maxwrite_sleep=" + st_maxwrite_sleep
+ + "\n st_map=" + st_map
+ + "\n st_cache_hit=" + st_cache_hit
+ + "\n st_cache_miss=" + st_cache_miss
+ + "\n st_page_create=" + st_page_create
+ + "\n st_page_in=" + st_page_in
+ + "\n st_page_out=" + st_page_out
+ + "\n st_ro_evict=" + st_ro_evict
+ + "\n st_rw_evict=" + st_rw_evict
+ + "\n st_page_trickle=" + st_page_trickle
+ + "\n st_pages=" + st_pages
+ + "\n st_page_clean=" + st_page_clean
+ + "\n st_page_dirty=" + st_page_dirty
+ + "\n st_hash_buckets=" + st_hash_buckets
+ + "\n st_hash_searches=" + st_hash_searches
+ + "\n st_hash_longest=" + st_hash_longest
+ + "\n st_hash_examined=" + st_hash_examined
+ + "\n st_hash_nowait=" + st_hash_nowait
+ + "\n st_hash_wait=" + st_hash_wait
+ + "\n st_hash_max_wait=" + st_hash_max_wait
+ + "\n st_region_nowait=" + st_region_nowait
+ + "\n st_region_wait=" + st_region_wait
+ + "\n st_alloc=" + st_alloc
+ + "\n st_alloc_buckets=" + st_alloc_buckets
+ + "\n st_alloc_max_buckets=" + st_alloc_max_buckets
+ + "\n st_alloc_pages=" + st_alloc_pages
+ + "\n st_alloc_max_pages=" + st_alloc_max_pages
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/CheckpointConfig.java b/db/java/src/com/sleepycat/db/CheckpointConfig.java
new file mode 100644
index 000000000..ab9d6bc83
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/CheckpointConfig.java
@@ -0,0 +1,60 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: CheckpointConfig.java,v 1.3 2004/04/21 01:09:09 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbEnv;
+import com.sleepycat.db.internal.DbConstants;
+
+public class CheckpointConfig {
+ public static final CheckpointConfig DEFAULT = new CheckpointConfig();
+
+ private boolean force = false;
+ private int kBytes = 0;
+ private int minutes = 0;
+
+ public CheckpointConfig() {
+ }
+
+ /* package */
+ static CheckpointConfig checkNull(CheckpointConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ public void setKBytes(final int kBytes) {
+ this.kBytes = kBytes;
+ }
+
+ public int getKBytes() {
+ return kBytes;
+ }
+
+ public void setMinutes(final int minutes) {
+ this.minutes = minutes;
+ }
+
+ public int getMinutes() {
+ return minutes;
+ }
+
+ public void setForce(final boolean force) {
+ this.force = force;
+ }
+
+ public boolean getForce() {
+ return force;
+ }
+
+ /* package */
+ void runCheckpoint(final DbEnv dbenv)
+ throws DatabaseException {
+
+ dbenv.txn_checkpoint(kBytes, minutes, force ? DbConstants.DB_FORCE : 0);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/Cursor.java b/db/java/src/com/sleepycat/db/Cursor.java
new file mode 100644
index 000000000..6b1e8e7f8
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/Cursor.java
@@ -0,0 +1,349 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: Cursor.java,v 1.5 2004/06/02 21:28:43 mark Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.Dbc;
+
+public class Cursor {
+ /* package */ Dbc dbc;
+ protected Database database;
+ protected CursorConfig config;
+
+ protected Cursor() {
+ }
+
+ Cursor(final Database database, final Dbc dbc, final CursorConfig config)
+ throws DatabaseException {
+
+ this.dbc = dbc;
+ this.database = database;
+ this.config = config;
+ }
+
+ public synchronized void close()
+ throws DatabaseException {
+
+ if (dbc != null) {
+ try {
+ dbc.close();
+ } finally {
+ dbc = null;
+ }
+ }
+ }
+
+ public Cursor dup(final boolean samePosition)
+ throws DatabaseException {
+
+ return new Cursor(database,
+ dbc.dup(samePosition ? DbConstants.DB_POSITION : 0), config);
+ }
+
+ public CursorConfig getConfig() {
+ return config;
+ }
+
+ public Database getDatabase() {
+ return database;
+ }
+
+ public int count()
+ throws DatabaseException {
+
+ return dbc.count(0);
+ }
+
+ public OperationStatus delete()
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(dbc.del(0));
+ }
+
+ public OperationStatus getCurrent(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_CURRENT |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getFirst(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_FIRST |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getLast(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_LAST |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getNext(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_NEXT |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getNextDup(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_NEXT_DUP |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getNextNoDup(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_NEXT_NODUP |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getPrev(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_PREV |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getPrevDup(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ /*
+ * "Get the previous duplicate" isn't directly supported by the C API,
+ * so here's how to get it: dup the cursor and call getPrev, then dup
+ * the result and call getNextDup. If both succeed then there was a
+ * previous duplicate and the first dup is sitting on it. Keep that,
+ * and call getCurrent to fill in the user's buffers.
+ */
+ Dbc dup1 = dbc.dup(DbConstants.DB_POSITION);
+ try {
+ int errCode = dup1.get(DatabaseEntry.IGNORE, DatabaseEntry.IGNORE,
+ DbConstants.DB_PREV | LockMode.getFlag(lockMode));
+ if (errCode == 0) {
+ Dbc dup2 = dup1.dup(DbConstants.DB_POSITION);
+ try {
+ errCode = dup2.get(DatabaseEntry.IGNORE,
+ DatabaseEntry.IGNORE,
+ DbConstants.DB_NEXT_DUP | LockMode.getFlag(lockMode));
+ } finally {
+ dup2.close();
+ }
+ }
+ if (errCode == 0)
+ errCode = dup1.get(key, data,
+ DbConstants.DB_CURRENT | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag()));
+ if (errCode == 0) {
+ Dbc tdbc = dbc;
+ dbc = dup1;
+ dup1 = tdbc;
+ }
+ return OperationStatus.fromInt(errCode);
+ } finally {
+ dup1.close();
+ }
+ }
+
+ public OperationStatus getPrevNoDup(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_PREV_NODUP |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getRecordNumber(final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(DatabaseEntry.IGNORE, data,
+ DbConstants.DB_GET_RECNO |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchKey(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_SET |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchKeyRange(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_SET_RANGE |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchBoth(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_GET_BOTH |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchBothRange(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data,
+ DbConstants.DB_GET_BOTH_RANGE |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchRecordNumber(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, DbConstants.DB_SET_RECNO |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus put(final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.put(key, data, DbConstants.DB_KEYLAST));
+ }
+
+ public OperationStatus putAfter(final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.put(DatabaseEntry.UNUSED, data, DbConstants.DB_AFTER));
+ }
+
+ public OperationStatus putBefore(final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.put(DatabaseEntry.UNUSED, data, DbConstants.DB_BEFORE));
+ }
+
+ public OperationStatus putNoOverwrite(final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ /*
+ * The tricks here are making sure the cursor doesn't move on error and
+ * noticing that if the key exists, that's an error and we don't want
+ * to return the data.
+ */
+ Dbc tempDbc = dbc.dup(0);
+ try {
+ int errCode = tempDbc.get(key, DatabaseEntry.IGNORE,
+ DbConstants.DB_SET | database.rmwFlag);
+ if (errCode == 0)
+ return OperationStatus.KEYEXIST;
+ else if (errCode != DbConstants.DB_NOTFOUND &&
+ errCode != DbConstants.DB_KEYEMPTY)
+ return OperationStatus.fromInt(errCode);
+ else {
+ Dbc tdbc = dbc;
+ dbc = tempDbc;
+ tempDbc = tdbc;
+
+ return OperationStatus.fromInt(
+ dbc.put(key, data, DbConstants.DB_KEYLAST));
+ }
+ } finally {
+ tempDbc.close();
+ }
+ }
+
+ public OperationStatus putKeyFirst(final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.put(key, data, DbConstants.DB_KEYFIRST));
+ }
+
+ public OperationStatus putKeyLast(final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.put(key, data, DbConstants.DB_KEYLAST));
+ }
+
+ public OperationStatus putNoDupData(final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.put(key, data, DbConstants.DB_NODUPDATA));
+ }
+
+ public OperationStatus putCurrent(final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.put(DatabaseEntry.UNUSED, data, DbConstants.DB_CURRENT));
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/CursorConfig.java b/db/java/src/com/sleepycat/db/CursorConfig.java
new file mode 100644
index 000000000..f674de3c8
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/CursorConfig.java
@@ -0,0 +1,76 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: CursorConfig.java,v 1.4 2004/09/28 19:30:36 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.Db;
+import com.sleepycat.db.internal.Dbc;
+import com.sleepycat.db.internal.DbTxn;
+
+public class CursorConfig implements Cloneable {
+ public static final CursorConfig DEFAULT = new CursorConfig();
+
+ public static final CursorConfig DIRTY_READ = new CursorConfig();
+ static { DIRTY_READ.setDirtyRead(true); }
+
+ public static final CursorConfig DEGREE_2 = new CursorConfig();
+ static { DEGREE_2.setDegree2(true); }
+
+ public static final CursorConfig WRITECURSOR = new CursorConfig();
+ static { WRITECURSOR.setWriteCursor(true); }
+
+
+ private boolean dirtyRead = false;
+ private boolean degree2 = false;
+ private boolean writeCursor = false;
+
+ public CursorConfig() {
+ }
+
+ /* package */
+ static CursorConfig checkNull(CursorConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ public void setDegree2(final boolean degree2) {
+ this.degree2 = degree2;
+ }
+
+ public boolean getDegree2() {
+ return degree2;
+ }
+
+ public void setDirtyRead(final boolean dirtyRead) {
+ this.dirtyRead = dirtyRead;
+ }
+
+ public boolean getDirtyRead() {
+ return dirtyRead;
+ }
+
+ public void setWriteCursor(final boolean writeCursor) {
+ this.writeCursor = writeCursor;
+ }
+
+ public boolean getWriteCursor() {
+ return writeCursor;
+ }
+
+ /* package */
+ Dbc openCursor(final Db db, final DbTxn txn)
+ throws DatabaseException {
+
+ int flags = 0;
+ flags |= dirtyRead ? DbConstants.DB_DIRTY_READ : 0;
+ flags |= degree2 ? DbConstants.DB_DEGREE_2 : 0;
+ flags |= writeCursor ? DbConstants.DB_WRITECURSOR : 0;
+ return db.cursor(txn, flags);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/Database.java b/db/java/src/com/sleepycat/db/Database.java
new file mode 100644
index 000000000..186e71e21
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/Database.java
@@ -0,0 +1,314 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: Database.java,v 1.12 2004/09/28 19:30:37 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.Db;
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbSequence;
+import com.sleepycat.db.internal.Dbc;
+
+public class Database {
+ Db db;
+ private int autoCommitFlag;
+ int rmwFlag;
+
+ /* package */
+ Database(final Db db)
+ throws DatabaseException {
+
+ this.db = db;
+ db.wrapper = this;
+ this.autoCommitFlag =
+ db.get_transactional() ? DbConstants.DB_AUTO_COMMIT : 0;
+ rmwFlag = ((db.get_env().get_open_flags() &
+ DbConstants.DB_INIT_LOCK) != 0) ? DbConstants.DB_RMW : 0;
+ }
+
+ public Database(final String filename,
+ final String databaseName,
+ final DatabaseConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ this(DatabaseConfig.checkNull(config).openDatabase(null, null,
+ filename, databaseName));
+ // Set up dbenv.wrapper
+ new Environment(db.get_env());
+ }
+
+ public void close(final boolean noSync)
+ throws DatabaseException {
+
+ db.close(noSync ? DbConstants.DB_NOSYNC : 0);
+ }
+
+ public void close()
+ throws DatabaseException {
+
+ close(false);
+ }
+
+ public Cursor openCursor(final Transaction txn, CursorConfig config)
+ throws DatabaseException {
+
+ return new Cursor(this, CursorConfig.checkNull(config).openCursor(
+ db, (txn == null) ? null : txn.txn), config);
+ }
+
+ public Sequence openSequence(final Transaction txn,
+ final DatabaseEntry key,
+ final SequenceConfig config)
+ throws DatabaseException {
+
+ return new Sequence(SequenceConfig.checkNull(config).openSequence(
+ db, (txn == null) ? null : txn.txn, key), config);
+ }
+
+ public void removeSequence(final Transaction txn,
+ final DatabaseEntry key,
+ SequenceConfig config)
+ throws DatabaseException {
+
+ config = SequenceConfig.checkNull(config);
+ final DbSequence seq = config.openSequence(
+ db, (txn == null) ? null : txn.txn, key);
+ seq.remove((txn == null) ? null : txn.txn,
+ (txn == null && db.get_transactional()) ?
+ DbConstants.DB_AUTO_COMMIT | (config.getAutoCommitNoSync() ?
+ DbConstants.DB_TXN_NOSYNC : 0) : 0);
+ }
+
+ public String getDatabaseFile()
+ throws DatabaseException {
+
+ return db.get_filename();
+ }
+
+ public String getDatabaseName()
+ throws DatabaseException {
+
+ return db.get_dbname();
+ }
+
+ public DatabaseConfig getConfig()
+ throws DatabaseException {
+
+ return new DatabaseConfig(db);
+ }
+
+ public void setConfig(DatabaseConfig config)
+ throws DatabaseException {
+
+ config.configureDatabase(db, getConfig());
+ }
+
+ public Environment getEnvironment()
+ throws DatabaseException {
+
+ return db.get_env().wrapper;
+ }
+
+ public CacheFile getCacheFile()
+ throws DatabaseException {
+
+ return new CacheFile(db.get_mpf());
+ }
+
+ public OperationStatus append(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.put((txn == null) ? null : txn.txn, key, data,
+ DbConstants.DB_APPEND | ((txn == null) ? autoCommitFlag : 0)));
+ }
+
+ public OperationStatus consume(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry data,
+ final boolean wait)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.get((txn == null) ? null : txn.txn,
+ key, data,
+ (wait ? DbConstants.DB_CONSUME_WAIT : DbConstants.DB_CONSUME) |
+ ((txn == null) ? autoCommitFlag : 0)));
+ }
+
+ public OperationStatus delete(final Transaction txn,
+ final DatabaseEntry key)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.del((txn == null) ? null : txn.txn, key,
+ ((txn == null) ? autoCommitFlag : 0)));
+ }
+
+ public OperationStatus get(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry data,
+ final LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.get((txn == null) ? null : txn.txn,
+ key, data,
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public KeyRange getKeyRange(final Transaction txn,
+ final DatabaseEntry key)
+ throws DatabaseException {
+
+ final KeyRange range = new KeyRange();
+ db.key_range((txn == null) ? null : txn.txn, key, range, 0);
+ return range;
+ }
+
+ public OperationStatus getSearchBoth(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry data,
+ final LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.get((txn == null) ? null : txn.txn,
+ key, data,
+ DbConstants.DB_GET_BOTH |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchRecordNumber(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry data,
+ final LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.get((txn == null) ? null : txn.txn,
+ key, data,
+ DbConstants.DB_SET_RECNO |
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus put(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.put((txn == null) ? null : txn.txn,
+ key, data,
+ ((txn == null) ? autoCommitFlag : 0)));
+ }
+
+ public OperationStatus putNoDupData(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.put((txn == null) ? null : txn.txn,
+ key, data,
+ DbConstants.DB_NODUPDATA |
+ ((txn == null) ? autoCommitFlag : 0)));
+ }
+
+ public OperationStatus putNoOverwrite(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.put((txn == null) ? null : txn.txn,
+ key, data,
+ DbConstants.DB_NOOVERWRITE |
+ ((txn == null) ? autoCommitFlag : 0)));
+ }
+
+ public JoinCursor join(final Cursor[] cursList, JoinConfig config)
+ throws DatabaseException {
+
+ config = JoinConfig.checkNull(config);
+
+ final Dbc[] dbcList = new Dbc[cursList.length];
+ for (int i = 0; i < cursList.length; i++)
+ dbcList[i] = (cursList[i] == null) ? null : cursList[i].dbc;
+
+ return new JoinCursor(this,
+ db.join(dbcList, config.getFlags()), config);
+ }
+
+ public int truncate(final Transaction txn, boolean countRecords)
+ throws DatabaseException {
+
+ // XXX: implement countRecords in C
+ int count = db.truncate((txn == null) ? null : txn.txn,
+ ((txn == null) ? autoCommitFlag : 0));
+
+ return countRecords ? count : -1;
+ }
+
+ public DatabaseStats getStats(final Transaction txn, StatsConfig config)
+ throws DatabaseException {
+
+ return (DatabaseStats)db.stat((txn == null) ? null : txn.txn,
+ StatsConfig.checkNull(config).getFlags());
+ }
+
+ public static void remove(final String fileName,
+ final String databaseName,
+ DatabaseConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ final Db db = DatabaseConfig.checkNull(config).createDatabase(null);
+ db.remove(fileName, databaseName, 0);
+ }
+
+ public static void rename(final String fileName,
+ final String oldDatabaseName,
+ final String newDatabaseName,
+ DatabaseConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ final Db db = DatabaseConfig.checkNull(config).createDatabase(null);
+ db.rename(fileName, oldDatabaseName, newDatabaseName, 0);
+ }
+
+ public void sync()
+ throws DatabaseException {
+
+ db.sync(0);
+ }
+
+ public static void upgrade(final String fileName,
+ DatabaseConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ final Db db = DatabaseConfig.checkNull(config).createDatabase(null);
+ db.upgrade(fileName,
+ config.getSortedDuplicates() ? DbConstants.DB_DUPSORT : 0);
+ db.close(0);
+ }
+
+ public boolean verify(final String fileName,
+ final String databaseName,
+ final java.io.PrintStream dumpStream,
+ VerifyConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ return db.verify(fileName, databaseName, dumpStream,
+ VerifyConfig.checkNull(config).getFlags());
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/DatabaseConfig.java b/db/java/src/com/sleepycat/db/DatabaseConfig.java
new file mode 100644
index 000000000..2a66f8410
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/DatabaseConfig.java
@@ -0,0 +1,628 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: DatabaseConfig.java,v 1.8 2004/07/30 14:52:21 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.Db;
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbEnv;
+import com.sleepycat.db.internal.DbTxn;
+import com.sleepycat.db.internal.DbUtil;
+
+public class DatabaseConfig implements Cloneable {
+ /*
+ * For internal use, final to allow null as a valid value for
+ * the config parameter.
+ */
+ public static final DatabaseConfig DEFAULT = new DatabaseConfig();
+
+ /* package */
+ static DatabaseConfig checkNull(DatabaseConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ /* Parameters */
+ private DatabaseType type = DatabaseType.UNKNOWN;
+ private int mode = 0644;
+ private int btMinKey = 0;
+ private int byteOrder = 0;
+ private long cacheSize = 0L;
+ private int cacheCount = 0;
+ private java.io.OutputStream errorStream = null;
+ private String errorPrefix = null;
+ private int hashFillFactor = 0;
+ private int hashNumElements = 0;
+ private java.io.OutputStream messageStream = null;
+ private int pageSize = 0;
+ private String password = null;
+ private int queueExtentSize = 0;
+ private int recordDelimiter = 0;
+ private int recordLength = 0;
+ private int recordPad = -1; // Zero is a valid, non-default value.
+ private java.io.File recordSource = null;
+
+ /* Flags */
+ private boolean allowCreate = false;
+ private boolean btreeRecordNumbers = false;
+ private boolean checksum = false;
+ private boolean dirtyRead = false;
+ private boolean encrypted = false;
+ private boolean exclusiveCreate = false;
+ private boolean noMMap = false;
+ private boolean queueInOrder = false;
+ private boolean readOnly = false;
+ private boolean renumbering = false;
+ private boolean reverseSplitOff = false;
+ private boolean sortedDuplicates = false;
+ private boolean snapshot = false;
+ private boolean unsortedDuplicates = false;
+ private boolean transactional = false;
+ private boolean transactionNotDurable = false;
+ private boolean truncate = false;
+ private boolean xaCreate = false;
+
+ private java.util.Comparator btreeComparator = null;
+ private BtreePrefixCalculator btreePrefixCalculator = null;
+ private java.util.Comparator duplicateComparator = null;
+ private FeedbackHandler feedbackHandler = null;
+ private ErrorHandler errorHandler = null;
+ private MessageHandler messageHandler = null;
+ private Hasher hasher = null;
+ private RecordNumberAppender recnoAppender = null;
+ private PanicHandler panicHandler = null;
+
+ public DatabaseConfig() {
+ }
+
+ public void setAllowCreate(final boolean allowCreate) {
+ this.allowCreate = allowCreate;
+ }
+
+ public boolean getAllowCreate() {
+ return allowCreate;
+ }
+
+ public void setBtreeComparator(final java.util.Comparator btreeComparator) {
+ this.btreeComparator = btreeComparator;
+ }
+
+ public java.util.Comparator getBtreeComparator() {
+ return btreeComparator;
+ }
+
+ public void setBtreeMinKey(final int btMinKey) {
+ this.btMinKey = btMinKey;
+ }
+
+ public int getBtreeMinKey() {
+ return btMinKey;
+ }
+
+ public void setByteOrder(final int byteOrder) {
+ this.byteOrder = byteOrder;
+ }
+
+ public int getByteOrder() {
+ return byteOrder;
+ }
+
+ public boolean getByteSwapped() {
+ return byteOrder != 0 && byteOrder != DbUtil.default_lorder();
+ }
+
+ public void setBtreePrefixCalculator(
+ final BtreePrefixCalculator btreePrefixCalculator) {
+ this.btreePrefixCalculator = btreePrefixCalculator;
+ }
+
+ public BtreePrefixCalculator getBtreePrefixCalculator() {
+ return btreePrefixCalculator;
+ }
+
+ public void setCacheSize(final long cacheSize) {
+ this.cacheSize = cacheSize;
+ }
+
+ public long getCacheSize() {
+ return cacheSize;
+ }
+
+ public void setCacheCount(final int cacheCount) {
+ this.cacheCount = cacheCount;
+ }
+
+ public int getCacheCount() {
+ return cacheCount;
+ }
+
+ public void setChecksum(final boolean checksum) {
+ this.checksum = checksum;
+ }
+
+ public boolean getChecksum() {
+ return checksum;
+ }
+
+ public void setDirtyRead(final boolean dirtyRead) {
+ this.dirtyRead = dirtyRead;
+ }
+
+ public boolean getDirtyRead() {
+ return dirtyRead;
+ }
+
+ public void setDuplicateComparator(final java.util.Comparator duplicateComparator) {
+ this.duplicateComparator = duplicateComparator;
+ }
+
+ public java.util.Comparator getDuplicateComparator() {
+ return duplicateComparator;
+ }
+
+ public void setEncrypted(final String password) {
+ this.password = password;
+ }
+
+ public boolean getEncrypted() {
+ return (password != null);
+ }
+
+ public void setErrorHandler(final ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ public ErrorHandler getErrorHandler() {
+ return errorHandler;
+ }
+
+ public void setErrorPrefix(final String errorPrefix) {
+ this.errorPrefix = errorPrefix;
+ }
+
+ public String getErrorPrefix() {
+ return errorPrefix;
+ }
+
+ public void setErrorStream(final java.io.OutputStream errorStream) {
+ this.errorStream = errorStream;
+ }
+
+ public java.io.OutputStream getErrorStream() {
+ return errorStream;
+ }
+
+ public void setExclusiveCreate(final boolean exclusiveCreate) {
+ this.exclusiveCreate = exclusiveCreate;
+ }
+
+ public boolean getExclusiveCreate() {
+ return exclusiveCreate;
+ }
+
+ public void setFeedbackHandler(final FeedbackHandler feedbackHandler) {
+ this.feedbackHandler = feedbackHandler;
+ }
+
+ public FeedbackHandler getFeedbackHandler() {
+ return feedbackHandler;
+ }
+
+ public void setHashFillFactor(final int hashFillFactor) {
+ this.hashFillFactor = hashFillFactor;
+ }
+
+ public int getHashFillFactor() {
+ return hashFillFactor;
+ }
+
+ public void setHasher(final Hasher hasher) {
+ this.hasher = hasher;
+ }
+
+ public Hasher getHasher() {
+ return hasher;
+ }
+
+ public void setHashNumElements(final int hashNumElements) {
+ this.hashNumElements = hashNumElements;
+ }
+
+ public int getHashNumElements() {
+ return hashNumElements;
+ }
+
+ public void setMessageHandler(final MessageHandler messageHandler) {
+ this.messageHandler = messageHandler;
+ }
+
+ public MessageHandler getMessageHandler() {
+ return messageHandler;
+ }
+
+ public void setMessageStream(final java.io.OutputStream messageStream) {
+ this.messageStream = messageStream;
+ }
+
+ public java.io.OutputStream getMessageStream() {
+ return messageStream;
+ }
+
+ public void setMode(final int mode) {
+ this.mode = mode;
+ }
+
+ public long getMode() {
+ return mode;
+ }
+
+ public void setNoMMap(final boolean noMMap) {
+ this.noMMap = noMMap;
+ }
+
+ public boolean getNoMMap() {
+ return noMMap;
+ }
+
+ public void setPageSize(final int pageSize) {
+ this.pageSize = pageSize;
+ }
+
+ public int getPageSize() {
+ return pageSize;
+ }
+
+ public void setPanicHandler(final PanicHandler panicHandler) {
+ this.panicHandler = panicHandler;
+ }
+
+ public PanicHandler getPanicHandler() {
+ return panicHandler;
+ }
+
+ public void setQueueExtentSize(final int queueExtentSize) {
+ this.queueExtentSize = queueExtentSize;
+ }
+
+ public int getQueueExtentSize() {
+ return queueExtentSize;
+ }
+
+ public void setQueueInOrder(final boolean queueInOrder) {
+ this.queueInOrder = queueInOrder;
+ }
+
+ public boolean getQueueInOrder() {
+ return queueInOrder;
+ }
+
+ public void setReadOnly(final boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ public boolean getReadOnly() {
+ return readOnly;
+ }
+
+ public void setRecordNumberAppender(final RecordNumberAppender recnoAppender) {
+ this.recnoAppender = recnoAppender;
+ }
+
+ public RecordNumberAppender getRecordNumberAppender() {
+ return recnoAppender;
+ }
+
+ public void setRecordDelimiter(final int recordDelimiter) {
+ this.recordDelimiter = recordDelimiter;
+ }
+
+ public int getRecordDelimiter() {
+ return recordDelimiter;
+ }
+
+ public void setRecordLength(final int recordLength) {
+ this.recordLength = recordLength;
+ }
+
+ public int getRecordLength() {
+ return recordLength;
+ }
+
+ public void setBtreeRecordNumbers(final boolean btreeRecordNumbers) {
+ this.btreeRecordNumbers = btreeRecordNumbers;
+ }
+
+ public boolean getBtreeRecordNumbers() {
+ return btreeRecordNumbers;
+ }
+
+ public void setRecordPad(final int recordPad) {
+ this.recordPad = recordPad;
+ }
+
+ public int getRecordPad() {
+ return recordPad;
+ }
+
+ public void setRecordSource(final java.io.File recordSource) {
+ this.recordSource = recordSource;
+ }
+
+ public java.io.File getRecordSource() {
+ return recordSource;
+ }
+
+ public void setRenumbering(final boolean renumbering) {
+ this.renumbering = renumbering;
+ }
+
+ public boolean getRenumbering() {
+ return renumbering;
+ }
+
+ public void setReverseSplitOff(final boolean reverseSplitOff) {
+ this.reverseSplitOff = reverseSplitOff;
+ }
+
+ public boolean getReverseSplitOff() {
+ return reverseSplitOff;
+ }
+
+ public void setSortedDuplicates(final boolean sortedDuplicates) {
+ this.sortedDuplicates = sortedDuplicates;
+ }
+
+ public boolean getSortedDuplicates() {
+ return sortedDuplicates;
+ }
+
+ public void setUnsortedDuplicates(final boolean unsortedDuplicates) {
+ this.unsortedDuplicates = unsortedDuplicates;
+ }
+
+ public boolean getUnsortedDuplicates() {
+ return unsortedDuplicates;
+ }
+
+ public void setSnapshot(final boolean snapshot) {
+ this.snapshot = snapshot;
+ }
+
+ public boolean getSnapshot() {
+ return snapshot;
+ }
+
+ public boolean getTransactional() {
+ return transactional;
+ }
+
+ public void setTransactional(final boolean transactional) {
+ this.transactional = transactional;
+ }
+
+ public void setTransactionNotDurable(final boolean transactionNotDurable) {
+ this.transactionNotDurable = transactionNotDurable;
+ }
+
+ public boolean getTransactionNotDurable() {
+ return transactionNotDurable;
+ }
+
+ public void setTruncate(final boolean truncate) {
+ this.truncate = truncate;
+ }
+
+ public boolean getTruncate() {
+ return truncate;
+ }
+
+ public void setType(final DatabaseType type) {
+ this.type = type;
+ }
+
+ public DatabaseType getType() {
+ return type;
+ }
+
+ public void setXACreate(final boolean xaCreate) {
+ this.xaCreate = xaCreate;
+ }
+
+ public boolean getXACreate() {
+ return xaCreate;
+ }
+
+ /* package */
+ Db createDatabase(final DbEnv dbenv)
+ throws DatabaseException {
+
+ int createFlags = 0;
+
+ createFlags |= xaCreate ? DbConstants.DB_XA_CREATE : 0;
+ return new Db(dbenv, createFlags);
+ }
+
+ /* package */
+ Db openDatabase(final DbEnv dbenv,
+ final DbTxn txn,
+ final String fileName,
+ final String databaseName)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ final Db db = createDatabase(dbenv);
+ // The DB_THREAD flag is inherited from the environment
+ // (defaulting to ON if no environment handle is supplied).
+ boolean threaded = (dbenv == null ||
+ (dbenv.get_open_flags() & DbConstants.DB_THREAD) != 0);
+
+ int openFlags = 0;
+ openFlags |= allowCreate ? DbConstants.DB_CREATE : 0;
+ openFlags |= dirtyRead ? DbConstants.DB_DIRTY_READ : 0;
+ openFlags |= exclusiveCreate ? DbConstants.DB_EXCL : 0;
+ openFlags |= noMMap ? DbConstants.DB_NOMMAP : 0;
+ openFlags |= readOnly ? DbConstants.DB_RDONLY : 0;
+ openFlags |= threaded ? DbConstants.DB_THREAD : 0;
+ openFlags |= truncate ? DbConstants.DB_TRUNCATE : 0;
+
+ if (transactional && txn == null)
+ openFlags |= DbConstants.DB_AUTO_COMMIT;
+
+ configureDatabase(db, DEFAULT);
+ boolean succeeded = false;
+ try {
+ db.open(txn, fileName, databaseName, type.getId(), openFlags, mode);
+ succeeded = true;
+ return db;
+ } finally {
+ if (!succeeded)
+ try {
+ db.close(0);
+ } catch (Throwable t) {
+ // Ignore it -- an exception is already in flight.
+ }
+ }
+ }
+
+ /* package */
+ void configureDatabase(final Db db, final DatabaseConfig oldConfig)
+ throws DatabaseException {
+
+ int dbFlags = 0;
+ dbFlags |= checksum ? DbConstants.DB_CHKSUM : 0;
+ dbFlags |= (password != null) ? DbConstants.DB_ENCRYPT : 0;
+ dbFlags |= btreeRecordNumbers ? DbConstants.DB_RECNUM : 0;
+ dbFlags |= queueInOrder ? DbConstants.DB_INORDER : 0;
+ dbFlags |= renumbering ? DbConstants.DB_RENUMBER : 0;
+ dbFlags |= reverseSplitOff ? DbConstants.DB_REVSPLITOFF : 0;
+ dbFlags |= sortedDuplicates ? DbConstants.DB_DUPSORT : 0;
+ dbFlags |= snapshot ? DbConstants.DB_SNAPSHOT : 0;
+ dbFlags |= unsortedDuplicates ? DbConstants.DB_DUP : 0;
+ dbFlags |= transactionNotDurable ? DbConstants.DB_TXN_NOT_DURABLE : 0;
+
+ if (dbFlags != 0)
+ db.set_flags(dbFlags);
+
+ if (btMinKey != oldConfig.btMinKey)
+ db.set_bt_minkey(btMinKey);
+ if (byteOrder != oldConfig.byteOrder)
+ db.set_lorder(byteOrder);
+ if (cacheSize != oldConfig.cacheSize ||
+ cacheCount != oldConfig.cacheCount)
+ db.set_cachesize(cacheSize, cacheCount);
+ if (errorStream != oldConfig.errorStream)
+ db.set_error_stream(errorStream);
+ if (errorPrefix != oldConfig.errorPrefix)
+ db.set_errpfx(errorPrefix);
+ if (hashFillFactor != oldConfig.hashFillFactor)
+ db.set_h_ffactor(hashFillFactor);
+ if (hashNumElements != oldConfig.hashNumElements)
+ db.set_h_nelem(hashNumElements);
+ if (messageStream != oldConfig.messageStream)
+ db.set_message_stream(messageStream);
+ if (pageSize != oldConfig.pageSize)
+ db.set_pagesize(pageSize);
+ if (password != oldConfig.password)
+ db.set_encrypt(password, DbConstants.DB_ENCRYPT_AES);
+ if (queueExtentSize != oldConfig.queueExtentSize)
+ db.set_q_extentsize(queueExtentSize);
+ if (recordDelimiter != oldConfig.recordDelimiter)
+ db.set_re_delim(recordDelimiter);
+ if (recordLength != oldConfig.recordLength)
+ db.set_re_len(recordLength);
+ if (recordPad != oldConfig.recordPad)
+ db.set_re_pad(recordPad);
+ if (recordSource != oldConfig.recordSource)
+ db.set_re_source(recordSource.toString());
+
+ if (btreeComparator != oldConfig.btreeComparator)
+ db.set_bt_compare(btreeComparator);
+ if (btreePrefixCalculator != oldConfig.btreePrefixCalculator)
+ db.set_bt_prefix(btreePrefixCalculator);
+ if (duplicateComparator != oldConfig.duplicateComparator)
+ db.set_dup_compare(duplicateComparator);
+ if (feedbackHandler != oldConfig.feedbackHandler)
+ db.set_feedback(feedbackHandler);
+ if (errorHandler != oldConfig.errorHandler)
+ db.set_errcall(errorHandler);
+ if (hasher != oldConfig.hasher)
+ db.set_h_hash(hasher);
+ if (messageHandler != oldConfig.messageHandler)
+ db.set_msgcall(messageHandler);
+ if (recnoAppender != oldConfig.recnoAppender)
+ db.set_append_recno(recnoAppender);
+ if (panicHandler != oldConfig.panicHandler)
+ db.set_paniccall(panicHandler);
+ }
+
+ /* package */
+ DatabaseConfig(final Db db)
+ throws DatabaseException {
+
+ type = DatabaseType.fromInt(db.get_type());
+
+ final int openFlags = db.get_open_flags();
+ allowCreate = (openFlags & DbConstants.DB_CREATE) != 0;
+ dirtyRead = (openFlags & DbConstants.DB_DIRTY_READ) != 0;
+ exclusiveCreate = (openFlags & DbConstants.DB_EXCL) != 0;
+ noMMap = (openFlags & DbConstants.DB_NOMMAP) != 0;
+ readOnly = (openFlags & DbConstants.DB_RDONLY) != 0;
+ truncate = (openFlags & DbConstants.DB_TRUNCATE) != 0;
+
+ final int dbFlags = db.get_flags();
+ checksum = (dbFlags & DbConstants.DB_CHKSUM) != 0;
+ btreeRecordNumbers = (dbFlags & DbConstants.DB_RECNUM) != 0;
+ queueInOrder = (dbFlags & DbConstants.DB_INORDER) != 0;
+ renumbering = (dbFlags & DbConstants.DB_RENUMBER) != 0;
+ reverseSplitOff = (dbFlags & DbConstants.DB_REVSPLITOFF) != 0;
+ sortedDuplicates = (dbFlags & DbConstants.DB_DUPSORT) != 0;
+ snapshot = (dbFlags & DbConstants.DB_SNAPSHOT) != 0;
+ unsortedDuplicates = (dbFlags & DbConstants.DB_DUP) != 0;
+ transactionNotDurable = (dbFlags & DbConstants.DB_TXN_NOT_DURABLE) != 0;
+
+ if (type == DatabaseType.BTREE) {
+ btMinKey = db.get_bt_minkey();
+ }
+ byteOrder = db.get_lorder();
+ // Call get_cachesize* on the DbEnv to avoid this error:
+ // DB->get_cachesize: method not permitted in shared environment
+ cacheSize = db.get_env().get_cachesize();
+ cacheCount = db.get_env().get_cachesize_ncache();
+ errorStream = db.get_error_stream();
+ errorPrefix = db.get_errpfx();
+ if (type == DatabaseType.HASH) {
+ hashFillFactor = db.get_h_ffactor();
+ hashNumElements = db.get_h_nelem();
+ }
+ messageStream = db.get_message_stream();
+ // Not available by design
+ password = ((dbFlags & DbConstants.DB_ENCRYPT) != 0) ? "" : null;
+ if (type == DatabaseType.QUEUE) {
+ queueExtentSize = db.get_q_extentsize();
+ }
+ if (type == DatabaseType.QUEUE || type == DatabaseType.RECNO) {
+ recordLength = db.get_re_len();
+ recordPad = db.get_re_pad();
+ }
+ if (type == DatabaseType.RECNO) {
+ recordDelimiter = db.get_re_delim();
+ recordSource = (db.get_re_source() == null) ? null :
+ new java.io.File(db.get_re_source());
+ }
+ transactional = db.get_transactional();
+
+ btreeComparator = db.get_bt_compare();
+ btreePrefixCalculator = db.get_bt_prefix();
+ duplicateComparator = db.get_dup_compare();
+ feedbackHandler = db.get_feedback();
+ errorHandler = db.get_errcall();
+ hasher = db.get_h_hash();
+ messageHandler = db.get_msgcall();
+ recnoAppender = db.get_append_recno();
+ panicHandler = db.get_paniccall();
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/DatabaseEntry.java b/db/java/src/com/sleepycat/db/DatabaseEntry.java
new file mode 100644
index 000000000..be67fe024
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/DatabaseEntry.java
@@ -0,0 +1,181 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: DatabaseEntry.java,v 1.7 2004/09/22 18:01:03 bostic Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbUtil;
+
+public class DatabaseEntry {
+
+ /* Currently, JE stores all data records as byte array */
+ protected byte[] data;
+ protected int dlen = 0;
+ protected int doff = 0;
+ protected int flags = 0;
+ protected int offset = 0;
+ protected int size = 0;
+ protected int ulen = 0;
+
+ /*
+ * IGNORE is used to avoid returning data that is not needed. It may not
+ * be used as the key DBT in a put since the PARTIAL flag is not allowed;
+ * use UNUSED for that instead.
+ */
+
+ /* package */
+ static final DatabaseEntry IGNORE = new DatabaseEntry();
+ static {
+ IGNORE.setUserBuffer(0, true);
+ IGNORE.setPartial(0, 0, true); // dlen == 0, so no data ever returned
+ }
+ /* package */
+ static final DatabaseEntry UNUSED = new DatabaseEntry();
+
+ protected static final int INT32SZ = 4;
+
+ /*
+ * Constructors
+ */
+
+ public DatabaseEntry() {
+ }
+
+ public DatabaseEntry(final byte[] data) {
+ this.data = data;
+ if (data != null) {
+ this.size = data.length;
+ }
+ }
+
+ public DatabaseEntry(final byte[] data, final int offset, final int size) {
+ this.data = data;
+ this.offset = offset;
+ this.size = size;
+ }
+
+ /*
+ * Accessors
+ */
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public void setData(final byte[] data, final int offset, final int size) {
+ this.data = data;
+ this.offset = offset;
+ this.size = size;
+ }
+
+ public void setData(final byte[] data) {
+ setData(data, 0, (data == null) ? 0 : data.length);
+ }
+
+ /* package */
+ int getMultiFlag() {
+ return 0;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(final int offset) {
+ this.offset = offset;
+ }
+
+ public int getPartialLength() {
+ return dlen;
+ }
+
+ public int getPartialOffset() {
+ return doff;
+ }
+
+ public boolean getPartial() {
+ return (flags & DbConstants.DB_DBT_PARTIAL) != 0;
+ }
+
+ public void setPartialOffset(final int doff) {
+ this.doff = doff;
+ }
+
+ public void setPartialLength(final int dlen) {
+ this.dlen = dlen;
+ }
+
+ public void setPartial(final boolean partial) {
+ if (partial)
+ flags |= DbConstants.DB_DBT_PARTIAL;
+ else
+ flags &= ~DbConstants.DB_DBT_PARTIAL;
+ }
+
+ public void setPartial(final int doff,
+ final int dlen,
+ final boolean partial) {
+ setPartialOffset(doff);
+ setPartialLength(dlen);
+ setPartial(partial);
+ }
+
+ public int getRecordNumber() {
+ return DbUtil.array2int(data, offset);
+ }
+
+ public void setRecordNumber(final int recno) {
+ if (data == null || data.length < INT32SZ) {
+ data = new byte[INT32SZ];
+ size = INT32SZ;
+ ulen = 0;
+ offset = 0;
+ }
+ DbUtil.int2array(recno, data, 0);
+ }
+
+ public boolean getReuseBuffer() {
+ return 0 ==
+ (flags & (DbConstants.DB_DBT_MALLOC | DbConstants.DB_DBT_USERMEM));
+ }
+
+ public void setReuseBuffer(boolean reuse) {
+ if (reuse)
+ flags &= ~(DbConstants.DB_DBT_MALLOC | DbConstants.DB_DBT_USERMEM);
+ else {
+ flags &= ~DbConstants.DB_DBT_USERMEM;
+ flags |= DbConstants.DB_DBT_MALLOC;
+ }
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setSize(final int size) {
+ this.size = size;
+ }
+
+ public boolean getUserBuffer() {
+ return (flags & DbConstants.DB_DBT_USERMEM) != 0;
+ }
+
+ public int getUserBufferLength() {
+ return ulen;
+ }
+
+ public void setUserBuffer(final int length, final boolean usermem) {
+ this.ulen = length;
+ if (usermem) {
+ flags &= ~DbConstants.DB_DBT_MALLOC;
+ flags |= DbConstants.DB_DBT_USERMEM;
+ } else
+ flags &= ~DbConstants.DB_DBT_USERMEM;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/DatabaseException.java b/db/java/src/com/sleepycat/db/DatabaseException.java
new file mode 100644
index 000000000..17ffb5432
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/DatabaseException.java
@@ -0,0 +1,54 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DatabaseException.java,v 1.1 2004/04/06 20:43:36 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbEnv;
+
+public class DatabaseException extends Exception {
+ private Environment dbenv;
+ private int errno;
+
+ public DatabaseException(final String s) {
+ this(s, 0, (Environment)null);
+ }
+
+ public DatabaseException(final String s, final int errno) {
+ this(s, errno, (Environment)null);
+ }
+
+ public DatabaseException(final String s,
+ final int errno,
+ final Environment dbenv) {
+ super(s);
+ this.errno = errno;
+ this.dbenv = dbenv;
+ }
+
+ protected DatabaseException(final String s,
+ final int errno,
+ final DbEnv dbenv) {
+ this(s, errno, (dbenv == null) ? null : dbenv.wrapper);
+ }
+
+ public Environment getEnvironment() {
+ return dbenv;
+ }
+
+ public int getErrno() {
+ return errno;
+ }
+
+ public String toString() {
+ String s = super.toString();
+ if (errno != 0)
+ s += ": " + DbEnv.strerror(errno);
+ return s;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/DatabaseStats.java b/db/java/src/com/sleepycat/db/DatabaseStats.java
new file mode 100644
index 000000000..c372ca435
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/DatabaseStats.java
@@ -0,0 +1,15 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DatabaseStats.java,v 1.2 2004/09/28 19:30:37 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public abstract class DatabaseStats {
+ // no public constructor
+ protected DatabaseStats() {}
+}
diff --git a/db/java/src/com/sleepycat/db/DatabaseType.java b/db/java/src/com/sleepycat/db/DatabaseType.java
new file mode 100644
index 000000000..2407498cf
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/DatabaseType.java
@@ -0,0 +1,65 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: DatabaseType.java,v 1.2 2004/04/21 01:09:09 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public final class DatabaseType {
+ public static final DatabaseType BTREE =
+ new DatabaseType("BTREE", DbConstants.DB_BTREE);
+
+ public static final DatabaseType HASH =
+ new DatabaseType("HASH", DbConstants.DB_HASH);
+
+ public static final DatabaseType QUEUE =
+ new DatabaseType("QUEUE", DbConstants.DB_QUEUE);
+
+ public static final DatabaseType RECNO =
+ new DatabaseType("RECNO", DbConstants.DB_RECNO);
+
+ public static final DatabaseType UNKNOWN =
+ new DatabaseType("UNKNOWN", DbConstants.DB_UNKNOWN);
+
+ /* package */
+ static DatabaseType fromInt(int type) {
+ switch(type) {
+ case DbConstants.DB_BTREE:
+ return BTREE;
+ case DbConstants.DB_HASH:
+ return HASH;
+ case DbConstants.DB_QUEUE:
+ return QUEUE;
+ case DbConstants.DB_RECNO:
+ return DatabaseType.RECNO;
+ case DbConstants.DB_UNKNOWN:
+ return DatabaseType.UNKNOWN;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown database type: " + type);
+ }
+ }
+
+ private String statusName;
+ private int id;
+
+ private DatabaseType(final String statusName, final int id) {
+ this.statusName = statusName;
+ this.id = id;
+ }
+
+ /* package */
+ int getId() {
+ return id;
+ }
+
+ public String toString() {
+ return "DatabaseType." + statusName;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/DeadlockException.java b/db/java/src/com/sleepycat/db/DeadlockException.java
new file mode 100644
index 000000000..afe9b498a
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/DeadlockException.java
@@ -0,0 +1,20 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DeadlockException.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbEnv;
+
+public class DeadlockException extends DatabaseException {
+ protected DeadlockException(final String s,
+ final int errno,
+ final DbEnv dbenv) {
+ super(s, errno, dbenv);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/Environment.java b/db/java/src/com/sleepycat/db/Environment.java
new file mode 100644
index 000000000..b45c56d58
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/Environment.java
@@ -0,0 +1,354 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: Environment.java,v 1.5 2004/08/05 19:20:34 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbEnv;
+
+public class Environment {
+ private DbEnv dbenv;
+ private java.io.File home;
+ private int autoCommitFlag;
+
+ /* package */
+ Environment(final DbEnv dbenv)
+ throws DatabaseException {
+
+ this.dbenv = dbenv;
+ dbenv.wrapper = this;
+ }
+
+ public Environment(final java.io.File home, EnvironmentConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ this(EnvironmentConfig.checkNull(config).openEnvironment(home));
+ this.home = home;
+ this.autoCommitFlag =
+ ((dbenv.get_open_flags() & DbConstants.DB_INIT_TXN) == 0) ? 0 :
+ DbConstants.DB_AUTO_COMMIT;
+ }
+
+ public void close()
+ throws DatabaseException {
+
+ dbenv.close(0);
+ }
+
+ /* package */
+ DbEnv unwrap() {
+ return dbenv;
+ }
+
+ public static void remove(final java.io.File home,
+ final boolean force,
+ EnvironmentConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ config = EnvironmentConfig.checkNull(config);
+ int flags = force ? DbConstants.DB_FORCE : 0;
+ flags |= config.getUseEnvironment() ?
+ DbConstants.DB_USE_ENVIRON : 0;
+ flags |= config.getUseEnvironmentRoot() ?
+ DbConstants.DB_USE_ENVIRON_ROOT : 0;
+ final DbEnv dbenv = config.createEnvironment();
+ dbenv.remove(home.toString(), flags);
+ }
+
+ public void setConfig(final EnvironmentConfig config)
+ throws DatabaseException {
+
+ config.configureEnvironment(dbenv, new EnvironmentConfig(dbenv));
+ }
+
+ public EnvironmentConfig getConfig()
+ throws DatabaseException {
+
+ return new EnvironmentConfig(dbenv);
+ }
+
+ /* Manage databases. */
+ public Database openDatabase(final Transaction txn,
+ final String fileName,
+ final String databaseName,
+ DatabaseConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ return new Database(
+ DatabaseConfig.checkNull(config).openDatabase(dbenv,
+ (txn == null) ? null : txn.txn,
+ fileName, databaseName));
+ }
+
+ public SecondaryDatabase openSecondaryDatabase(
+ final Transaction txn,
+ final String fileName,
+ final String databaseName,
+ final Database primaryDatabase,
+ SecondaryConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ return new SecondaryDatabase(
+ SecondaryConfig.checkNull(config).openSecondaryDatabase(
+ dbenv, (txn == null) ? null : txn.txn,
+ fileName, databaseName, primaryDatabase.db),
+ primaryDatabase);
+ }
+
+ public void removeDatabase(final Transaction txn,
+ final String fileName,
+ final String databaseName)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ dbenv.dbremove((txn == null) ? null : txn.txn,
+ fileName, databaseName,
+ (txn == null) ? autoCommitFlag : 0);
+ }
+
+ public void renameDatabase(final Transaction txn,
+ final String fileName,
+ final String databaseName,
+ final String newName)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ dbenv.dbrename((txn == null) ? null : txn.txn,
+ fileName, databaseName, newName,
+ (txn == null) ? autoCommitFlag : 0);
+ }
+
+ public java.io.File getHome()
+ throws DatabaseException {
+
+ return home;
+ }
+
+ /* Cache management. */
+ public int trickleCacheWrite(int percent)
+ throws DatabaseException {
+
+ return dbenv.memp_trickle(percent);
+ }
+
+ /* Locking */
+ public int detectDeadlocks(LockDetectMode mode)
+ throws DatabaseException {
+
+ return dbenv.lock_detect(0, mode.getFlag());
+ }
+
+ public Lock getLock(int locker,
+ boolean noWait,
+ DatabaseEntry object,
+ LockRequestMode mode)
+ throws DatabaseException {
+
+ return Lock.wrap(
+ dbenv.lock_get(locker, noWait ? DbConstants.DB_LOCK_NOWAIT : 0,
+ object, mode.getFlag()));
+ }
+
+ public void putLock(Lock lock)
+ throws DatabaseException {
+
+ dbenv.lock_put(lock.unwrap());
+ }
+
+ public int createLockerID()
+ throws DatabaseException {
+
+ return dbenv.lock_id();
+ }
+
+ public void freeLockerID(int id)
+ throws DatabaseException {
+
+ dbenv.lock_id_free(id);
+ }
+
+ public void lockVector(int locker, boolean noWait, LockRequest[] list)
+ throws DatabaseException {
+
+ dbenv.lock_vec(locker, noWait ? DbConstants.DB_LOCK_NOWAIT : 0,
+ list, 0, list.length);
+ }
+
+ /* Logging */
+ public LogCursor openLogCursor()
+ throws DatabaseException {
+
+ return LogCursor.wrap(dbenv.log_cursor(0));
+ }
+
+ public String getLogFileName(LogSequenceNumber lsn)
+ throws DatabaseException {
+
+ return dbenv.log_file(lsn);
+ }
+
+ /* Replication support */
+ public int electReplicationMaster(int nsites,
+ int nvotes,
+ int priority,
+ int timeout)
+ throws DatabaseException {
+
+ return dbenv.rep_elect(nsites, nvotes, priority, timeout,
+ 0 /* unused flags */);
+ }
+
+ public ReplicationStatus processReplicationMessage(DatabaseEntry control,
+ DatabaseEntry rec,
+ int envid)
+ throws DatabaseException {
+
+ final DbEnv.RepProcessMessage wrappedID = new DbEnv.RepProcessMessage();
+ wrappedID.envid = envid;
+ // Create a new entry so that rec isn't overwritten
+ final DatabaseEntry cdata =
+ new DatabaseEntry(rec.getData(), rec.getOffset(), rec.getSize());
+ final LogSequenceNumber lsn = new LogSequenceNumber();
+ final int ret =
+ dbenv.rep_process_message(control, cdata, wrappedID, lsn);
+ return ReplicationStatus.getStatus(ret, cdata, wrappedID.envid, lsn);
+ }
+
+ public void startReplication(DatabaseEntry cdata, boolean master)
+ throws DatabaseException {
+
+ dbenv.rep_start(cdata,
+ master ? DbConstants.DB_REP_MASTER : DbConstants.DB_REP_CLIENT);
+ }
+
+ /* Statistics */
+ public CacheStats getCacheStats(StatsConfig config)
+ throws DatabaseException {
+
+ return dbenv.memp_stat(StatsConfig.checkNull(config).getFlags());
+ }
+
+ public CacheFileStats[] getCacheFileStats(StatsConfig config)
+ throws DatabaseException {
+
+ return dbenv.memp_fstat(StatsConfig.checkNull(config).getFlags());
+ }
+
+ public LogStats getLogStats(StatsConfig config)
+ throws DatabaseException {
+
+ return dbenv.log_stat(StatsConfig.checkNull(config).getFlags());
+ }
+
+ public ReplicationStats getReplicationStats(StatsConfig config)
+ throws DatabaseException {
+
+ return dbenv.rep_stat(StatsConfig.checkNull(config).getFlags());
+ }
+
+ public LockStats getLockStats(StatsConfig config)
+ throws DatabaseException {
+
+ return dbenv.lock_stat(StatsConfig.checkNull(config).getFlags());
+ }
+
+ public TransactionStats getTransactionStats(StatsConfig config)
+ throws DatabaseException {
+
+ return dbenv.txn_stat(StatsConfig.checkNull(config).getFlags());
+ }
+
+ /* Transaction management */
+ public Transaction beginTransaction(final Transaction parent,
+ TransactionConfig config)
+ throws DatabaseException {
+
+ return new Transaction(
+ TransactionConfig.checkNull(config).beginTransaction(dbenv,
+ (parent == null) ? null : parent.txn));
+ }
+
+ public void checkpoint(CheckpointConfig config)
+ throws DatabaseException {
+
+ CheckpointConfig.checkNull(config).runCheckpoint(dbenv);
+ }
+
+ public void logFlush(LogSequenceNumber lsn)
+ throws DatabaseException {
+
+ dbenv.log_flush(lsn);
+ }
+
+ public LogSequenceNumber logPut(DatabaseEntry data, boolean flush)
+ throws DatabaseException {
+
+ final LogSequenceNumber lsn = new LogSequenceNumber();
+ dbenv.log_put(lsn, data, flush ? DbConstants.DB_FLUSH : 0);
+ return lsn;
+ }
+
+ public java.io.File[] getArchiveLogFiles(boolean includeInUse)
+ throws DatabaseException {
+
+ final String[] logNames =
+ dbenv.log_archive(DbConstants.DB_ARCH_ABS |
+ (includeInUse ? DbConstants.DB_ARCH_LOG : 0));
+ final java.io.File[] logFiles = new java.io.File[logNames.length];
+ for (int i = 0; i < logNames.length; i++)
+ logFiles[i] = new java.io.File(logNames[i]);
+ return logFiles;
+ }
+
+ public java.io.File[] getArchiveDatabases()
+ throws DatabaseException {
+
+ final String[] dbNames = dbenv.log_archive(DbConstants.DB_ARCH_DATA);
+ final java.io.File[] dbFiles = new java.io.File[dbNames.length];
+ for (int i = 0; i < dbNames.length; i++)
+ dbFiles[i] = new java.io.File(home, dbNames[i]);
+ return dbFiles;
+ }
+
+ public void removeOldLogFiles()
+ throws DatabaseException {
+
+ dbenv.log_archive(DbConstants.DB_ARCH_REMOVE);
+ }
+
+ public PreparedTransaction[] recover(final int count,
+ final boolean continued)
+ throws DatabaseException {
+
+ return dbenv.txn_recover(count,
+ continued ? DbConstants.DB_NEXT : DbConstants.DB_FIRST);
+ }
+
+ /* Panic the environment, or stop a panic. */
+ public void panic(boolean onoff)
+ throws DatabaseException {
+
+ dbenv.set_flags(DbConstants.DB_PANIC_ENVIRONMENT, onoff);
+ }
+
+ /* Version information */
+ public static String getVersionString() {
+ return DbEnv.get_version_string();
+ }
+
+ public static int getVersionMajor() {
+ return DbEnv.get_version_major();
+ }
+
+ public static int getVersionMinor() {
+ return DbEnv.get_version_minor();
+ }
+
+ public static int getVersionPatch() {
+ return DbEnv.get_version_patch();
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/EnvironmentConfig.java b/db/java/src/com/sleepycat/db/EnvironmentConfig.java
new file mode 100644
index 000000000..23e6ab309
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/EnvironmentConfig.java
@@ -0,0 +1,1076 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: EnvironmentConfig.java,v 1.13 2004/09/28 19:30:37 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbEnv;
+
+public class EnvironmentConfig implements Cloneable {
+ /*
+ * For internal use, to allow null as a valid value for
+ * the config parameter.
+ */
+ public static final EnvironmentConfig DEFAULT = new EnvironmentConfig();
+
+ /* package */
+ static EnvironmentConfig checkNull(EnvironmentConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ /* Parameters */
+ private int mode = 0644;
+ private int cacheCount = 0;
+ private long cacheSize = 0L;
+ private java.util.Vector dataDirs = new java.util.Vector();
+ private int envid = 0;
+ private java.io.OutputStream errorStream = null;
+ private String errorPrefix = null;
+ private byte[][] lockConflicts = null;
+ private LockDetectMode lockDetectMode = LockDetectMode.NONE;
+ private int maxLocks = 0;
+ private int maxLockers = 0;
+ private int maxLockObjects = 0;
+ private int maxLogFileSize = 0;
+ private int logBufferSize = 0;
+ private java.io.OutputStream messageStream = null;
+ private java.io.File logDirectory = null;
+ private int logRegionSize = 0;
+ private long mmapSize = 0L;
+ private String password = null;
+ private long replicationLimit = 0L;
+ private String rpcServer = null;
+ private long rpcClientTimeout = 0L;
+ private long rpcServerTimeout = 0L;
+ private long segmentId = 0L;
+ private int testAndSetSpins = 0;
+ private long lockTimeout = 0L;
+ private int txnMaxActive = 0;
+ private long txnTimeout = 0L;
+ private java.util.Date txnTimestamp = null;
+ private String temporaryDirectory = null;
+
+ /* Open flags */
+ private boolean allowCreate = false;
+ private boolean initializeCache = false;
+ private boolean initializeCDB = false;
+ private boolean initializeLocking = false;
+ private boolean initializeLogging = false;
+ private boolean initializeReplication = false;
+ private boolean joinEnvironment = false;
+ private boolean lockDown = false;
+ private boolean isPrivate = false;
+ private boolean readOnly = false;
+ private boolean runRecovery = false;
+ private boolean runFatalRecovery = false;
+ private boolean systemMemory = false;
+ private boolean threaded = true; // Handles are threaded by default in Java
+ private boolean transactional = false;
+ private boolean useEnvironment = false;
+ private boolean useEnvironmentRoot = false;
+
+ /* Flags */
+ private boolean cdbLockAllDatabases = false;
+ private boolean directDatabaseIO = false;
+ private boolean directLogIO = false;
+ private boolean dsyncLog = false;
+ private boolean initializeRegions = false;
+ private boolean logAutoRemove = false;
+ private boolean logInMemory = false;
+ private boolean noLocking = false;
+ private boolean noMMap = false;
+ private boolean noPanic = false;
+ private boolean overwrite = false;
+ private boolean txnNoSync = false;
+ private boolean txnNotDurable = false;
+ private boolean txnWriteNoSync = false;
+ private boolean yieldCPU = false;
+
+ /* Verbose Flags */
+ private boolean verboseDeadlock = false;
+ private boolean verboseRecovery = false;
+ private boolean verboseReplication = false;
+ private boolean verboseWaitsFor = false;
+
+ /* Callbacks */
+ private ErrorHandler errorHandler = null;
+ private FeedbackHandler feedbackHandler = null;
+ private LogRecordHandler logRecordHandler = null;
+ private MessageHandler messageHandler = null;
+ private PanicHandler panicHandler = null;
+ private ReplicationTransport replicationTransport = null;
+
+ public EnvironmentConfig() {
+ }
+
+ public void setAllowCreate(final boolean allowCreate) {
+ this.allowCreate = allowCreate;
+ }
+
+ public boolean getAllowCreate() {
+ return allowCreate;
+ }
+
+ public void setCacheSize(final long cacheSize) {
+ this.cacheSize = cacheSize;
+ }
+
+ public long getCacheSize() {
+ return cacheSize;
+ }
+
+ public void setCacheCount(final int cacheCount) {
+ this.cacheCount = cacheCount;
+ }
+
+ public int getCacheCount() {
+ return cacheCount;
+ }
+
+ public void setCDBLockAllDatabases(final boolean cdbLockAllDatabases) {
+ this.cdbLockAllDatabases = cdbLockAllDatabases;
+ }
+
+ public boolean getCDBLockAllDatabases() {
+ return cdbLockAllDatabases;
+ }
+
+ public void addDataDir(final String dataDir) {
+ this.dataDirs.add(dataDir);
+ }
+
+ public String[] getDataDirs() {
+ final String[] dirs = new String[dataDirs.size()];
+ dataDirs.copyInto(dirs);
+ return dirs;
+ }
+
+ public void setDirectDatabaseIO(final boolean directDatabaseIO) {
+ this.directDatabaseIO = directDatabaseIO;
+ }
+
+ public boolean getDirectDatabaseIO() {
+ return directDatabaseIO;
+ }
+
+ public void setDirectLogIO(final boolean directLogIO) {
+ this.directLogIO = directLogIO;
+ }
+
+ public boolean getDirectLogIO() {
+ return directLogIO;
+ }
+
+ public void setDsyncLog(final boolean dsyncLog) {
+ this.dsyncLog = dsyncLog;
+ }
+
+ public boolean getDsyncLog() {
+ return dsyncLog;
+ }
+
+ public void setEncrypted(final String password) {
+ this.password = password;
+ }
+
+ public boolean getEncrypted() {
+ return (password != null);
+ }
+
+ public void setErrorHandler(final ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ public ErrorHandler getErrorHandler() {
+ return errorHandler;
+ }
+
+ public void setErrorPrefix(final String errorPrefix) {
+ this.errorPrefix = errorPrefix;
+ }
+
+ public String getErrorPrefix() {
+ return errorPrefix;
+ }
+
+ public void setErrorStream(final java.io.OutputStream errorStream) {
+ this.errorStream = errorStream;
+ }
+
+ public java.io.OutputStream getErrorStream() {
+ return errorStream;
+ }
+
+ public void setFeedbackHandler(final FeedbackHandler feedbackHandler) {
+ this.feedbackHandler = feedbackHandler;
+ }
+
+ public FeedbackHandler getFeedbackHandler() {
+ return feedbackHandler;
+ }
+
+ public void setInitializeCache(final boolean initializeCache) {
+ this.initializeCache = initializeCache;
+ }
+
+ public boolean getInitializeCache() {
+ return initializeCache;
+ }
+
+ public void setInitializeCDB(final boolean initializeCDB) {
+ this.initializeCDB = initializeCDB;
+ }
+
+ public boolean getInitializeCDB() {
+ return initializeCDB;
+ }
+
+ public void setInitializeLocking(final boolean initializeLocking) {
+ this.initializeLocking = initializeLocking;
+ }
+
+ public boolean getInitializeLocking() {
+ return initializeLocking;
+ }
+
+ public void setInitializeLogging(final boolean initializeLogging) {
+ this.initializeLogging = initializeLogging;
+ }
+
+ public boolean getInitializeLogging() {
+ return initializeLogging;
+ }
+
+ public void setInitializeRegions(final boolean initializeRegions) {
+ this.initializeRegions = initializeRegions;
+ }
+
+ public boolean getInitializeRegions() {
+ return initializeRegions;
+ }
+
+ public void setInitializeReplication(final boolean initializeReplication) {
+ this.initializeReplication = initializeReplication;
+ }
+
+ public boolean getInitializeReplication() {
+ return initializeReplication;
+ }
+
+ public void setJoinEnvironment(final boolean joinEnvironment) {
+ this.joinEnvironment = joinEnvironment;
+ }
+
+ public boolean getJoinEnvironment() {
+ return joinEnvironment;
+ }
+
+ public void setLockConflicts(final byte[][] lockConflicts) {
+ this.lockConflicts = lockConflicts;
+ }
+
+ public byte[][] getLockConflicts() {
+ return lockConflicts;
+ }
+
+ public void setLockDetectMode(final LockDetectMode lockDetectMode) {
+ this.lockDetectMode = lockDetectMode;
+ }
+
+ public LockDetectMode getLockDetectMode() {
+ return lockDetectMode;
+ }
+
+ public void setLockDown(final boolean lockDown) {
+ this.lockDown = lockDown;
+ }
+
+ public boolean getLockDown() {
+ return lockDown;
+ }
+
+ public void setLockTimeout(final long lockTimeout) {
+ this.lockTimeout = lockTimeout;
+ }
+
+ public long getLockTimeout() {
+ return lockTimeout;
+ }
+
+ public void setLogAutoRemove(final boolean logAutoRemove) {
+ this.logAutoRemove = logAutoRemove;
+ }
+
+ public boolean getLogAutoRemove() {
+ return logAutoRemove;
+ }
+
+ public void setLogInMemory(final boolean logInMemory) {
+ this.logInMemory = logInMemory;
+ }
+
+ public boolean getLogInMemory() {
+ return logInMemory;
+ }
+
+ public void setLogRecordHandler(final LogRecordHandler logRecordHandler) {
+ this.logRecordHandler = logRecordHandler;
+ }
+
+ public LogRecordHandler getLogRecordHandler() {
+ return logRecordHandler;
+ }
+
+ public void setMaxLocks(final int maxLocks) {
+ this.maxLocks = maxLocks;
+ }
+
+ public int getMaxLocks() {
+ return maxLocks;
+ }
+
+ public void setMaxLockers(final int maxLockers) {
+ this.maxLockers = maxLockers;
+ }
+
+ public int getMaxLockers() {
+ return maxLockers;
+ }
+
+ public void setMaxLockObjects(final int maxLockObjects) {
+ this.maxLockObjects = maxLockObjects;
+ }
+
+ public int getMaxLockObjects() {
+ return maxLockObjects;
+ }
+
+ public void setMaxLogFileSize(final int maxLogFileSize) {
+ this.maxLogFileSize = maxLogFileSize;
+ }
+
+ public int getMaxLogFileSize() {
+ return maxLogFileSize;
+ }
+
+ public void setLogBufferSize(final int logBufferSize) {
+ this.logBufferSize = logBufferSize;
+ }
+
+ public int getLogBufferSize() {
+ return logBufferSize;
+ }
+
+ public void setLogDirectory(final java.io.File logDirectory) {
+ this.logDirectory = logDirectory;
+ }
+
+ public java.io.File getLogDirectory() {
+ return logDirectory;
+ }
+
+ public void setLogRegionSize(final int logRegionSize) {
+ this.logRegionSize = logRegionSize;
+ }
+
+ public int getLogRegionSize() {
+ return logRegionSize;
+ }
+
+ public void setMessageHandler(final MessageHandler messageHandler) {
+ this.messageHandler = messageHandler;
+ }
+
+ public MessageHandler getMessageHandler() {
+ return messageHandler;
+ }
+
+ public void setMessageStream(final java.io.OutputStream messageStream) {
+ this.messageStream = messageStream;
+ }
+
+ public java.io.OutputStream getMessageStream() {
+ return messageStream;
+ }
+
+ public void setMMapSize(final long mmapSize) {
+ this.mmapSize = mmapSize;
+ }
+
+ public long getMMapSize() {
+ return mmapSize;
+ }
+
+ public void setMode(final int mode) {
+ this.mode = mode;
+ }
+
+ public long getMode() {
+ return mode;
+ }
+
+ public void setNoLocking(final boolean noLocking) {
+ this.noLocking = noLocking;
+ }
+
+ public boolean getNoLocking() {
+ return noLocking;
+ }
+
+ public void setNoMMap(final boolean noMMap) {
+ this.noMMap = noMMap;
+ }
+
+ public boolean getNoMMap() {
+ return noMMap;
+ }
+
+ public void setNoPanic(final boolean noPanic) {
+ this.noPanic = noPanic;
+ }
+
+ public boolean getNoPanic() {
+ return noPanic;
+ }
+
+ public void setOverwrite(final boolean overwrite) {
+ this.overwrite = overwrite;
+ }
+
+ public boolean getOverwrite() {
+ return overwrite;
+ }
+
+ public void setPanicHandler(final PanicHandler panicHandler) {
+ this.panicHandler = panicHandler;
+ }
+
+ public PanicHandler getPanicHandler() {
+ return panicHandler;
+ }
+
+ public void setPrivate(final boolean isPrivate) {
+ this.isPrivate = isPrivate;
+ }
+
+ public boolean getPrivate() {
+ return isPrivate;
+ }
+
+ public boolean getReadOnly() {
+ return readOnly;
+ }
+
+ public void setReadOnly(final boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ public void setReplicationLimit(final long replicationLimit) {
+ this.replicationLimit = replicationLimit;
+ }
+
+ public long getReplicationLimit() {
+ return replicationLimit;
+ }
+
+ public void setReplicationTransport(final int envid,
+ final ReplicationTransport replicationTransport) {
+
+ this.envid = envid;
+ this.replicationTransport = replicationTransport;
+ }
+
+ public ReplicationTransport getReplicationTransport() {
+ return replicationTransport;
+ }
+
+ public void setRunFatalRecovery(final boolean runFatalRecovery) {
+ this.runFatalRecovery = runFatalRecovery;
+ }
+
+ public boolean getRunFatalRecovery() {
+ return runFatalRecovery;
+ }
+
+ public void setRunRecovery(final boolean runRecovery) {
+ this.runRecovery = runRecovery;
+ }
+
+ public boolean getRunRecovery() {
+ return runRecovery;
+ }
+
+ public void setSystemMemory(final boolean systemMemory) {
+ this.systemMemory = systemMemory;
+ }
+
+ public boolean getSystemMemory() {
+ return systemMemory;
+ }
+
+ public void setRPCServer(final String rpcServer,
+ final long rpcClientTimeout,
+ final long rpcServerTimeout) {
+ this.rpcServer = rpcServer;
+ this.rpcClientTimeout = rpcClientTimeout;
+ this.rpcServerTimeout = rpcServerTimeout;
+
+ // Turn off threading for RPC client handles.
+ this.threaded = false;
+ }
+
+ public void setSegmentId(final long segmentId) {
+ this.segmentId = segmentId;
+ }
+
+ public long getSegmentId() {
+ return segmentId;
+ }
+
+ public void setTemporaryDirectory(final String temporaryDirectory) {
+ this.temporaryDirectory = temporaryDirectory;
+ }
+
+ public String getTemporaryDirectory() {
+ return temporaryDirectory;
+ }
+
+ public void setTestAndSetSpins(final int testAndSetSpins) {
+ this.testAndSetSpins = testAndSetSpins;
+ }
+
+ public int getTestAndSetSpins() {
+ return testAndSetSpins;
+ }
+
+ public void setThreaded(final boolean threaded) {
+ this.threaded = threaded;
+ }
+
+ public boolean getThreaded() {
+ return threaded;
+ }
+
+ public void setTransactional(final boolean transactional) {
+ this.transactional = transactional;
+ }
+
+ public boolean getTransactional() {
+ return transactional;
+ }
+
+ public void setTxnNoSync(final boolean txnNoSync) {
+ this.txnNoSync = txnNoSync;
+ }
+
+ public boolean getTxnNoSync() {
+ return txnNoSync;
+ }
+
+ public void setTxnNotDurable(final boolean txnNotDurable) {
+ this.txnNotDurable = txnNotDurable;
+ }
+
+ public boolean getTxnNotDurable() {
+ return txnNotDurable;
+ }
+
+ public void setTxnMaxActive(final int txnMaxActive) {
+ this.txnMaxActive = txnMaxActive;
+ }
+
+ public int getTxnMaxActive() {
+ return txnMaxActive;
+ }
+
+ public void setTxnTimeout(final long txnTimeout) {
+ this.txnTimeout = txnTimeout;
+ }
+
+ public long getTxnTimeout() {
+ return txnTimeout;
+ }
+
+ public void setTxnTimestamp(final java.util.Date txnTimestamp) {
+ this.txnTimestamp = txnTimestamp;
+ }
+
+ public java.util.Date getTxnTimestamp() {
+ return txnTimestamp;
+ }
+
+ public void setTxnWriteNoSync(final boolean txnWriteNoSync) {
+ this.txnWriteNoSync = txnWriteNoSync;
+ }
+
+ public boolean getTxnWriteNoSync() {
+ return txnWriteNoSync;
+ }
+
+ public void setUseEnvironment(final boolean useEnvironment) {
+ this.useEnvironment = useEnvironment;
+ }
+
+ public boolean getUseEnvironment() {
+ return useEnvironment;
+ }
+
+ public void setUseEnvironmentRoot(final boolean useEnvironmentRoot) {
+ this.useEnvironmentRoot = useEnvironmentRoot;
+ }
+
+ public boolean getUseEnvironmentRoot() {
+ return useEnvironmentRoot;
+ }
+
+ public void setVerboseDeadlock(final boolean verboseDeadlock) {
+ this.verboseDeadlock = verboseDeadlock;
+ }
+
+ public boolean getVerboseDeadlock() {
+ return verboseDeadlock;
+ }
+
+ public void setVerboseRecovery(final boolean verboseRecovery) {
+ this.verboseRecovery = verboseRecovery;
+ }
+
+ public boolean getVerboseRecovery() {
+ return verboseRecovery;
+ }
+
+ public void setVerboseReplication(final boolean verboseReplication) {
+ this.verboseReplication = verboseReplication;
+ }
+
+ public boolean getVerboseReplication() {
+ return verboseReplication;
+ }
+
+ public void setVerboseWaitsFor(final boolean verboseWaitsFor) {
+ this.verboseWaitsFor = verboseWaitsFor;
+ }
+
+ public boolean getVerboseWaitsFor() {
+ return verboseWaitsFor;
+ }
+
+ public void setYieldCPU(final boolean yieldCPU) {
+ this.yieldCPU = yieldCPU;
+ }
+
+ public boolean getYieldCPU() {
+ return yieldCPU;
+ }
+
+ private boolean lockConflictsEqual(byte[][] lc1, byte[][]lc2) {
+ if (lc1 == lc2)
+ return true;
+ if (lc1 == null || lc2 == null || lc1.length != lc2.length)
+ return false;
+ for (int i = 0; i < lc1.length; i++) {
+ if (lc1[i].length != lc2[i].length)
+ return false;
+ for (int j = 0; j < lc1[i].length; j++)
+ if (lc1[i][j] != lc2[i][j])
+ return false;
+ }
+ return true;
+ }
+
+ /* package */
+ DbEnv openEnvironment(final java.io.File home)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ final DbEnv dbenv = createEnvironment();
+ int openFlags = 0;
+
+ openFlags |= allowCreate ? DbConstants.DB_CREATE : 0;
+ openFlags |= initializeCache ? DbConstants.DB_INIT_MPOOL : 0;
+ openFlags |= initializeCDB ? DbConstants.DB_INIT_CDB : 0;
+ openFlags |= initializeLocking ? DbConstants.DB_INIT_LOCK : 0;
+ openFlags |= initializeLogging ? DbConstants.DB_INIT_LOG : 0;
+ openFlags |= initializeReplication ? DbConstants.DB_INIT_REP : 0;
+ openFlags |= joinEnvironment ? DbConstants.DB_JOINENV : 0;
+ openFlags |= lockDown ? DbConstants.DB_LOCKDOWN : 0;
+ openFlags |= isPrivate ? DbConstants.DB_PRIVATE : 0;
+ openFlags |= readOnly ? DbConstants.DB_RDONLY : 0;
+ openFlags |= runRecovery ? DbConstants.DB_RECOVER : 0;
+ openFlags |= runFatalRecovery ? DbConstants.DB_RECOVER_FATAL : 0;
+ openFlags |= systemMemory ? DbConstants.DB_SYSTEM_MEM : 0;
+ openFlags |= threaded ? DbConstants.DB_THREAD : 0;
+ openFlags |= transactional ? DbConstants.DB_INIT_TXN : 0;
+ openFlags |= useEnvironment ? DbConstants.DB_USE_ENVIRON : 0;
+ openFlags |= useEnvironmentRoot ? DbConstants.DB_USE_ENVIRON_ROOT : 0;
+
+ boolean succeeded = false;
+ try {
+ dbenv.open(home.toString(), openFlags, mode);
+ succeeded = true;
+ return dbenv;
+ } finally {
+ if (!succeeded)
+ try {
+ dbenv.close(0);
+ } catch (Throwable t) {
+ // Ignore it -- an exception is already in flight.
+ }
+ }
+ }
+
+
+ /* package */
+ DbEnv createEnvironment()
+ throws DatabaseException {
+
+ int createFlags = 0;
+
+ if (rpcServer != null)
+ createFlags |= DbConstants.DB_RPCCLIENT;
+
+ final DbEnv dbenv = new DbEnv(createFlags);
+ configureEnvironment(dbenv, DEFAULT);
+ return dbenv;
+ }
+
+ /* package */
+ void configureEnvironment(final DbEnv dbenv,
+ final EnvironmentConfig oldConfig)
+ throws DatabaseException {
+
+ if (errorHandler != oldConfig.errorHandler)
+ dbenv.set_errcall(errorHandler);
+ if (errorPrefix != oldConfig.errorPrefix &&
+ errorPrefix != null && !errorPrefix.equals(oldConfig.errorPrefix))
+ dbenv.set_errpfx(errorPrefix);
+ if (errorStream != oldConfig.errorStream)
+ dbenv.set_error_stream(errorStream);
+
+ if (rpcServer != oldConfig.rpcServer ||
+ rpcClientTimeout != oldConfig.rpcClientTimeout ||
+ rpcServerTimeout != oldConfig.rpcServerTimeout)
+ dbenv.set_rpc_server(null, rpcServer,
+ rpcClientTimeout, rpcServerTimeout, 0);
+
+ // We always set DB_TIME_NOTGRANTED in the Java API, because
+ // LockNotGrantedException extends DeadlockException, so there's no
+ // reason why an application would prefer one to the other.
+ int onFlags = DbConstants.DB_TIME_NOTGRANTED;
+ int offFlags = 0;
+
+ if (cdbLockAllDatabases && !oldConfig.cdbLockAllDatabases)
+ onFlags |= DbConstants.DB_CDB_ALLDB;
+ if (!cdbLockAllDatabases && oldConfig.cdbLockAllDatabases)
+ offFlags |= DbConstants.DB_CDB_ALLDB;
+
+ if (directDatabaseIO && !oldConfig.directDatabaseIO)
+ onFlags |= DbConstants.DB_DIRECT_DB;
+ if (!directDatabaseIO && oldConfig.directDatabaseIO)
+ offFlags |= DbConstants.DB_DIRECT_DB;
+
+ if (directLogIO && !oldConfig.directLogIO)
+ onFlags |= DbConstants.DB_DIRECT_LOG;
+ if (!directLogIO && oldConfig.directLogIO)
+ offFlags |= DbConstants.DB_DIRECT_LOG;
+
+ if (dsyncLog && !oldConfig.dsyncLog)
+ onFlags |= DbConstants.DB_DSYNC_LOG;
+ if (!dsyncLog && oldConfig.dsyncLog)
+ offFlags |= DbConstants.DB_DSYNC_LOG;
+
+ if (initializeRegions && !oldConfig.initializeRegions)
+ onFlags |= DbConstants.DB_REGION_INIT;
+ if (!initializeRegions && oldConfig.initializeRegions)
+ offFlags |= DbConstants.DB_REGION_INIT;
+
+ if (logAutoRemove && !oldConfig.logAutoRemove)
+ onFlags |= DbConstants.DB_LOG_AUTOREMOVE;
+ if (!logAutoRemove && oldConfig.logAutoRemove)
+ offFlags |= DbConstants.DB_LOG_AUTOREMOVE;
+
+ if (logInMemory && !oldConfig.logInMemory)
+ onFlags |= DbConstants.DB_LOG_INMEMORY;
+ if (!logInMemory && oldConfig.logInMemory)
+ offFlags |= DbConstants.DB_LOG_INMEMORY;
+
+ if (noLocking && !oldConfig.noLocking)
+ onFlags |= DbConstants.DB_NOLOCKING;
+ if (!noLocking && oldConfig.noLocking)
+ offFlags |= DbConstants.DB_NOLOCKING;
+
+ if (noMMap && !oldConfig.noMMap)
+ onFlags |= DbConstants.DB_NOMMAP;
+ if (!noMMap && oldConfig.noMMap)
+ offFlags |= DbConstants.DB_NOMMAP;
+
+ if (noPanic && !oldConfig.noPanic)
+ onFlags |= DbConstants.DB_NOPANIC;
+ if (!noPanic && oldConfig.noPanic)
+ offFlags |= DbConstants.DB_NOPANIC;
+
+ if (overwrite && !oldConfig.overwrite)
+ onFlags |= DbConstants.DB_OVERWRITE;
+ if (!overwrite && oldConfig.overwrite)
+ offFlags |= DbConstants.DB_OVERWRITE;
+
+ if (txnNoSync && !oldConfig.txnNoSync)
+ onFlags |= DbConstants.DB_TXN_NOSYNC;
+ if (!txnNoSync && oldConfig.txnNoSync)
+ offFlags |= DbConstants.DB_TXN_NOSYNC;
+
+ if (txnNotDurable && !oldConfig.txnNotDurable)
+ onFlags |= DbConstants.DB_TXN_NOT_DURABLE;
+ if (!txnNotDurable && oldConfig.txnNotDurable)
+ offFlags |= DbConstants.DB_TXN_NOT_DURABLE;
+
+ if (txnWriteNoSync && !oldConfig.txnWriteNoSync)
+ onFlags |= DbConstants.DB_TXN_WRITE_NOSYNC;
+ if (!txnWriteNoSync && oldConfig.txnWriteNoSync)
+ offFlags |= DbConstants.DB_TXN_WRITE_NOSYNC;
+
+ if (yieldCPU && !oldConfig.yieldCPU)
+ onFlags |= DbConstants.DB_YIELDCPU;
+ if (!yieldCPU && oldConfig.yieldCPU)
+ offFlags |= DbConstants.DB_YIELDCPU;
+
+ if (onFlags != 0)
+ dbenv.set_flags(onFlags, true);
+ if (offFlags != 0)
+ dbenv.set_flags(offFlags, false);
+
+ /* Verbose flags */
+ onFlags = 0;
+ offFlags = 0;
+
+ if (verboseDeadlock && !oldConfig.verboseDeadlock)
+ onFlags |= DbConstants.DB_VERB_DEADLOCK;
+ if (!verboseDeadlock && oldConfig.verboseDeadlock)
+ offFlags |= DbConstants.DB_VERB_DEADLOCK;
+
+ if (verboseRecovery && !oldConfig.verboseRecovery)
+ onFlags |= DbConstants.DB_VERB_RECOVERY;
+ if (!verboseRecovery && oldConfig.verboseRecovery)
+ offFlags |= DbConstants.DB_VERB_RECOVERY;
+
+ if (verboseReplication && !oldConfig.verboseReplication)
+ onFlags |= DbConstants.DB_VERB_REPLICATION;
+ if (!verboseReplication && oldConfig.verboseReplication)
+ offFlags |= DbConstants.DB_VERB_REPLICATION;
+
+ if (verboseWaitsFor && !oldConfig.verboseWaitsFor)
+ onFlags |= DbConstants.DB_VERB_WAITSFOR;
+ if (!verboseWaitsFor && oldConfig.verboseWaitsFor)
+ offFlags |= DbConstants.DB_VERB_WAITSFOR;
+
+ if (onFlags != 0)
+ dbenv.set_verbose(onFlags, true);
+ if (offFlags != 0)
+ dbenv.set_verbose(offFlags, false);
+
+ /* Callbacks */
+ if (feedbackHandler != oldConfig.feedbackHandler)
+ dbenv.set_feedback(feedbackHandler);
+ if (logRecordHandler != oldConfig.logRecordHandler)
+ dbenv.set_app_dispatch(logRecordHandler);
+ if (messageHandler != oldConfig.messageHandler)
+ dbenv.set_msgcall(messageHandler);
+ if (panicHandler != oldConfig.panicHandler)
+ dbenv.set_paniccall(panicHandler);
+ if (replicationTransport != oldConfig.replicationTransport)
+ dbenv.set_rep_transport(envid, replicationTransport);
+
+ /* Other settings */
+ if (cacheSize != oldConfig.cacheSize ||
+ cacheCount != oldConfig.cacheCount)
+ dbenv.set_cachesize(cacheSize, cacheCount);
+ for (final java.util.Enumeration e = dataDirs.elements();
+ e.hasMoreElements();) {
+ final String dir = (String)e.nextElement();
+ if (!oldConfig.dataDirs.contains(dir))
+ dbenv.set_data_dir(dir);
+ }
+ if (!lockConflictsEqual(lockConflicts, oldConfig.lockConflicts))
+ dbenv.set_lk_conflicts(lockConflicts);
+ if (lockDetectMode != oldConfig.lockDetectMode)
+ dbenv.set_lk_detect(lockDetectMode.getFlag());
+ if (maxLocks != oldConfig.maxLocks)
+ dbenv.set_lk_max_locks(maxLocks);
+ if (maxLockers != oldConfig.maxLockers)
+ dbenv.set_lk_max_lockers(maxLockers);
+ if (maxLockObjects != oldConfig.maxLockObjects)
+ dbenv.set_lk_max_objects(maxLockObjects);
+ if (maxLogFileSize != oldConfig.maxLogFileSize)
+ dbenv.set_lg_max(maxLogFileSize);
+ if (logBufferSize != oldConfig.logBufferSize)
+ dbenv.set_lg_bsize(logBufferSize);
+ if (logDirectory != oldConfig.logDirectory && logDirectory != null &&
+ !logDirectory.equals(oldConfig.logDirectory))
+ dbenv.set_lg_dir(logDirectory.toString());
+ if (logRegionSize != oldConfig.logRegionSize)
+ dbenv.set_lg_regionmax(logRegionSize);
+ if (messageStream != oldConfig.messageStream)
+ dbenv.set_message_stream(messageStream);
+ if (mmapSize != oldConfig.mmapSize)
+ dbenv.set_mp_mmapsize(mmapSize);
+ if (password != null)
+ dbenv.set_encrypt(password, DbConstants.DB_ENCRYPT_AES);
+ if (replicationLimit != oldConfig.replicationLimit)
+ dbenv.set_rep_limit(replicationLimit);
+ if (segmentId != oldConfig.segmentId)
+ dbenv.set_shm_key(segmentId);
+ if (testAndSetSpins != oldConfig.testAndSetSpins)
+ dbenv.set_tas_spins(testAndSetSpins);
+ if (lockTimeout != oldConfig.lockTimeout)
+ dbenv.set_timeout(lockTimeout, DbConstants.DB_SET_LOCK_TIMEOUT);
+ if (txnMaxActive != oldConfig.txnMaxActive)
+ dbenv.set_tx_max(txnMaxActive);
+ if (txnTimeout != oldConfig.txnTimeout)
+ dbenv.set_timeout(txnTimeout, DbConstants.DB_SET_TXN_TIMEOUT);
+ if (txnTimestamp != oldConfig.txnTimestamp && txnTimestamp != null &&
+ !txnTimestamp.equals(oldConfig.txnTimestamp))
+ dbenv.set_tx_timestamp(txnTimestamp);
+ if (temporaryDirectory != oldConfig.temporaryDirectory &&
+ temporaryDirectory != null &&
+ !temporaryDirectory.equals(oldConfig.temporaryDirectory))
+ dbenv.set_tmp_dir(temporaryDirectory);
+ }
+
+ /* package */
+ EnvironmentConfig(final DbEnv dbenv)
+ throws DatabaseException {
+
+ final int openFlags = dbenv.get_open_flags();
+
+ allowCreate = ((openFlags & DbConstants.DB_CREATE) != 0);
+ initializeCache = ((openFlags & DbConstants.DB_INIT_MPOOL) != 0);
+ initializeCDB = ((openFlags & DbConstants.DB_INIT_CDB) != 0);
+ initializeLocking = ((openFlags & DbConstants.DB_INIT_LOCK) != 0);
+ initializeLogging = ((openFlags & DbConstants.DB_INIT_LOG) != 0);
+ initializeReplication = ((openFlags & DbConstants.DB_INIT_REP) != 0);
+ joinEnvironment = ((openFlags & DbConstants.DB_JOINENV) != 0);
+ lockDown = ((openFlags & DbConstants.DB_LOCKDOWN) != 0);
+ isPrivate = ((openFlags & DbConstants.DB_PRIVATE) != 0);
+ readOnly = ((openFlags & DbConstants.DB_RDONLY) != 0);
+ runRecovery = ((openFlags & DbConstants.DB_RECOVER) != 0);
+ runFatalRecovery = ((openFlags & DbConstants.DB_RECOVER_FATAL) != 0);
+ systemMemory = ((openFlags & DbConstants.DB_SYSTEM_MEM) != 0);
+ threaded = ((openFlags & DbConstants.DB_THREAD) != 0);
+ transactional = ((openFlags & DbConstants.DB_INIT_TXN) != 0);
+ useEnvironment = ((openFlags & DbConstants.DB_USE_ENVIRON) != 0);
+ useEnvironmentRoot =
+ ((openFlags & DbConstants.DB_USE_ENVIRON_ROOT) != 0);
+
+ final int envFlags = dbenv.get_flags();
+
+ cdbLockAllDatabases = ((envFlags & DbConstants.DB_CDB_ALLDB) != 0);
+ directDatabaseIO = ((envFlags & DbConstants.DB_DIRECT_DB) != 0);
+ directLogIO = ((envFlags & DbConstants.DB_DIRECT_LOG) != 0);
+ dsyncLog = ((envFlags & DbConstants.DB_DSYNC_LOG) != 0);
+ initializeRegions = ((envFlags & DbConstants.DB_REGION_INIT) != 0);
+ logAutoRemove = ((envFlags & DbConstants.DB_LOG_AUTOREMOVE) != 0);
+ logInMemory = ((envFlags & DbConstants.DB_LOG_INMEMORY) != 0);
+ noLocking = ((envFlags & DbConstants.DB_NOLOCKING) != 0);
+ noMMap = ((envFlags & DbConstants.DB_NOMMAP) != 0);
+ noPanic = ((envFlags & DbConstants.DB_NOPANIC) != 0);
+ overwrite = ((envFlags & DbConstants.DB_OVERWRITE) != 0);
+ txnNoSync = ((envFlags & DbConstants.DB_TXN_NOSYNC) != 0);
+ txnNotDurable = ((envFlags & DbConstants.DB_TXN_NOT_DURABLE) != 0);
+ txnWriteNoSync = ((envFlags & DbConstants.DB_TXN_WRITE_NOSYNC) != 0);
+ yieldCPU = ((envFlags & DbConstants.DB_YIELDCPU) != 0);
+
+ /* Verbose flags */
+ verboseDeadlock = dbenv.get_verbose(DbConstants.DB_VERB_DEADLOCK);
+ verboseRecovery = dbenv.get_verbose(DbConstants.DB_VERB_RECOVERY);
+ verboseReplication = dbenv.get_verbose(DbConstants.DB_VERB_REPLICATION);
+ verboseWaitsFor = dbenv.get_verbose(DbConstants.DB_VERB_WAITSFOR);
+
+ /* Callbacks */
+ errorHandler = dbenv.get_errcall();
+ feedbackHandler = dbenv.get_feedback();
+ logRecordHandler = dbenv.get_app_dispatch();
+ messageHandler = dbenv.get_msgcall();
+ panicHandler = dbenv.get_paniccall();
+ // XXX: replicationTransport and envid aren't available?
+
+ /* Other settings */
+ cacheSize = dbenv.get_cachesize();
+ cacheCount = dbenv.get_cachesize_ncache();
+
+ String[] dataDirArray = dbenv.get_data_dirs();
+ if (dataDirArray == null)
+ dataDirArray = new String[0];
+ dataDirs = new java.util.Vector(dataDirArray.length);
+ for (int i = 0; i < dataDirArray.length; i++)
+ dataDirs.set(i, dataDirArray[i]);
+
+ errorPrefix = dbenv.get_errpfx();
+ errorStream = dbenv.get_error_stream();
+
+ if (initializeLocking) {
+ lockConflicts = dbenv.get_lk_conflicts();
+ lockDetectMode = LockDetectMode.fromFlag(dbenv.get_lk_detect());
+ lockTimeout = dbenv.get_timeout(DbConstants.DB_SET_LOCK_TIMEOUT);
+ maxLocks = dbenv.get_lk_max_locks();
+ maxLockers = dbenv.get_lk_max_lockers();
+ maxLockObjects = dbenv.get_lk_max_objects();
+ txnTimeout = dbenv.get_timeout(DbConstants.DB_SET_TXN_TIMEOUT);
+ } else {
+ lockConflicts = null;
+ lockDetectMode = LockDetectMode.NONE;
+ lockTimeout = 0L;
+ maxLocks = 0;
+ maxLockers = 0;
+ maxLockObjects = 0;
+ txnTimeout = 0L;
+ }
+ if (initializeLogging) {
+ maxLogFileSize = dbenv.get_lg_max();
+ logBufferSize = dbenv.get_lg_bsize();
+ logDirectory = (dbenv.get_lg_dir() == null) ? null :
+ new java.io.File(dbenv.get_lg_dir());
+ logRegionSize = dbenv.get_lg_regionmax();
+ } else {
+ maxLogFileSize = 0;
+ logBufferSize = 0;
+ logDirectory = null;
+ logRegionSize = 0;
+ }
+ messageStream = dbenv.get_message_stream();
+ mmapSize = dbenv.get_mp_mmapsize();
+
+ // XXX: intentional information loss?
+ password = (dbenv.get_encrypt_flags() == 0) ? null : "";
+
+ if (initializeReplication) {
+ replicationLimit = dbenv.get_rep_limit();
+ } else {
+ replicationLimit = 0L;
+ }
+
+ // XXX: no way to find RPC server?
+ rpcServer = null;
+ rpcClientTimeout = 0;
+ rpcServerTimeout = 0;
+
+ segmentId = dbenv.get_shm_key();
+ testAndSetSpins = dbenv.get_tas_spins();
+ if (transactional) {
+ txnMaxActive = dbenv.get_tx_max();
+ final long txnTimestampSeconds = dbenv.get_tx_timestamp();
+ if (txnTimestampSeconds != 0L)
+ txnTimestamp = new java.util.Date(txnTimestampSeconds * 1000);
+ else
+ txnTimestamp = null;
+ } else {
+ txnMaxActive = 0;
+ txnTimestamp = null;
+ }
+ temporaryDirectory = dbenv.get_tmp_dir();
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/ErrorHandler.java b/db/java/src/com/sleepycat/db/ErrorHandler.java
new file mode 100644
index 000000000..ad4a9e5c4
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/ErrorHandler.java
@@ -0,0 +1,14 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ErrorHandler.java,v 1.2 2004/04/20 20:45:11 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public interface ErrorHandler {
+ void error(Environment dbenv, String errpfx, String msg);
+}
diff --git a/db/java/src/com/sleepycat/db/FeedbackHandler.java b/db/java/src/com/sleepycat/db/FeedbackHandler.java
new file mode 100644
index 000000000..96b2e6eb2
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/FeedbackHandler.java
@@ -0,0 +1,16 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: FeedbackHandler.java,v 1.2 2004/04/21 01:09:09 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public interface FeedbackHandler {
+ void recoveryFeedback(Environment dbenv, int percent);
+ void upgradeFeedback(Database db, int percent);
+ void verifyFeedback(Database db, int percent);
+}
diff --git a/db/java/src/com/sleepycat/db/HashStats.java b/db/java/src/com/sleepycat/db/HashStats.java
new file mode 100644
index 000000000..3e04452b7
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/HashStats.java
@@ -0,0 +1,116 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class HashStats extends DatabaseStats {
+ // no public constructor
+ protected HashStats() {}
+
+ private int hash_magic;
+ public int getMagic() {
+ return hash_magic;
+ }
+
+ private int hash_version;
+ public int getVersion() {
+ return hash_version;
+ }
+
+ private int hash_metaflags;
+ public int getMetaFlags() {
+ return hash_metaflags;
+ }
+
+ private int hash_nkeys;
+ public int getNumKeys() {
+ return hash_nkeys;
+ }
+
+ private int hash_ndata;
+ public int getNumData() {
+ return hash_ndata;
+ }
+
+ private int hash_pagesize;
+ public int getPageSize() {
+ return hash_pagesize;
+ }
+
+ private int hash_ffactor;
+ public int getFfactor() {
+ return hash_ffactor;
+ }
+
+ private int hash_buckets;
+ public int getBuckets() {
+ return hash_buckets;
+ }
+
+ private int hash_free;
+ public int getFree() {
+ return hash_free;
+ }
+
+ private int hash_bfree;
+ public int getBFree() {
+ return hash_bfree;
+ }
+
+ private int hash_bigpages;
+ public int getBigPages() {
+ return hash_bigpages;
+ }
+
+ private int hash_big_bfree;
+ public int getBigBFree() {
+ return hash_big_bfree;
+ }
+
+ private int hash_overflows;
+ public int getOverflows() {
+ return hash_overflows;
+ }
+
+ private int hash_ovfl_free;
+ public int getOvflFree() {
+ return hash_ovfl_free;
+ }
+
+ private int hash_dup;
+ public int getDup() {
+ return hash_dup;
+ }
+
+ private int hash_dup_free;
+ public int getDupFree() {
+ return hash_dup_free;
+ }
+
+ public String toString() {
+ return "HashStats:"
+ + "\n hash_magic=" + hash_magic
+ + "\n hash_version=" + hash_version
+ + "\n hash_metaflags=" + hash_metaflags
+ + "\n hash_nkeys=" + hash_nkeys
+ + "\n hash_ndata=" + hash_ndata
+ + "\n hash_pagesize=" + hash_pagesize
+ + "\n hash_ffactor=" + hash_ffactor
+ + "\n hash_buckets=" + hash_buckets
+ + "\n hash_free=" + hash_free
+ + "\n hash_bfree=" + hash_bfree
+ + "\n hash_bigpages=" + hash_bigpages
+ + "\n hash_big_bfree=" + hash_big_bfree
+ + "\n hash_overflows=" + hash_overflows
+ + "\n hash_ovfl_free=" + hash_ovfl_free
+ + "\n hash_dup=" + hash_dup
+ + "\n hash_dup_free=" + hash_dup_free
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/Hasher.java b/db/java/src/com/sleepycat/db/Hasher.java
new file mode 100644
index 000000000..cc46a8549
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/Hasher.java
@@ -0,0 +1,14 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: Hasher.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public interface Hasher {
+ int hash(Database db, byte[] data, int len);
+}
diff --git a/db/java/src/com/sleepycat/db/JoinConfig.java b/db/java/src/com/sleepycat/db/JoinConfig.java
new file mode 100644
index 000000000..41689767c
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/JoinConfig.java
@@ -0,0 +1,39 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: JoinConfig.java,v 1.4 2004/09/28 19:30:37 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public class JoinConfig implements Cloneable {
+ public static final JoinConfig DEFAULT = new JoinConfig();
+
+ /* package */
+ static JoinConfig checkNull(JoinConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ private boolean noSort;
+
+ public JoinConfig() {
+ }
+
+ public void setNoSort(final boolean noSort) {
+ this.noSort = noSort;
+ }
+
+ public boolean getNoSort() {
+ return noSort;
+ }
+
+ /* package */
+ int getFlags() {
+ return noSort ? DbConstants.DB_JOIN_NOSORT : 0;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/JoinCursor.java b/db/java/src/com/sleepycat/db/JoinCursor.java
new file mode 100644
index 000000000..5527a6177
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/JoinCursor.java
@@ -0,0 +1,60 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: JoinCursor.java,v 1.2 2004/04/09 15:08:38 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.Dbc;
+
+public class JoinCursor {
+ private Database database;
+ private Dbc dbc;
+ private JoinConfig config;
+
+ JoinCursor(final Database database,
+ final Dbc dbc,
+ final JoinConfig config) {
+ this.database = database;
+ this.dbc = dbc;
+ this.config = config;
+ }
+
+ public void close()
+ throws DatabaseException {
+
+ dbc.close();
+ }
+
+ public Database getDatabase() {
+ return database;
+ }
+
+ public JoinConfig getConfig() {
+ return config;
+ }
+
+ public OperationStatus getNext(final DatabaseEntry key, LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, DatabaseEntry.IGNORE,
+ DbConstants.DB_JOIN_ITEM |
+ LockMode.getFlag(lockMode)));
+ }
+
+ public OperationStatus getNext(final DatabaseEntry key,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.get(key, data, LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/KeyRange.java b/db/java/src/com/sleepycat/db/KeyRange.java
new file mode 100644
index 000000000..5ddbb123d
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/KeyRange.java
@@ -0,0 +1,16 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: KeyRange.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public class KeyRange {
+ public double equal;
+ public double greater;
+ public double less;
+}
diff --git a/db/java/src/com/sleepycat/db/Lock.java b/db/java/src/com/sleepycat/db/Lock.java
new file mode 100644
index 000000000..64559e359
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/Lock.java
@@ -0,0 +1,31 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: Lock.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbLock;
+
+public final class Lock {
+ private DbLock dbLock;
+
+ private Lock(final DbLock dblock) {
+ this.dbLock = dbLock;
+ dbLock.wrapper = this;
+ }
+
+ /* package */
+ static Lock wrap(final DbLock dblock) {
+ return (dblock == null) ? null : new Lock(dblock);
+ }
+
+ /* package */
+ DbLock unwrap() {
+ return dbLock;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LockDetectMode.java b/db/java/src/com/sleepycat/db/LockDetectMode.java
new file mode 100644
index 000000000..ac163cad3
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LockDetectMode.java
@@ -0,0 +1,90 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: LockDetectMode.java,v 1.2 2004/04/21 01:09:09 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public final class LockDetectMode {
+ public static final LockDetectMode NONE =
+ new LockDetectMode("NONE", 0);
+
+ public static final LockDetectMode DEFAULT =
+ new LockDetectMode("DEFAULT", DbConstants.DB_LOCK_DEFAULT);
+
+ public static final LockDetectMode EXPIRE =
+ new LockDetectMode("EXPIRE", DbConstants.DB_LOCK_EXPIRE);
+
+ public static final LockDetectMode MAXLOCKS =
+ new LockDetectMode("MAXLOCKS", DbConstants.DB_LOCK_MAXLOCKS);
+
+ public static final LockDetectMode MAXWRITE =
+ new LockDetectMode("MAXWRITE", DbConstants.DB_LOCK_MAXWRITE);
+
+ public static final LockDetectMode MINLOCKS =
+ new LockDetectMode("MINLOCKS", DbConstants.DB_LOCK_MINLOCKS);
+
+ public static final LockDetectMode MINWRITE =
+ new LockDetectMode("MINWRITE", DbConstants.DB_LOCK_MINWRITE);
+
+ public static final LockDetectMode OLDEST =
+ new LockDetectMode("OLDEST", DbConstants.DB_LOCK_OLDEST);
+
+ public static final LockDetectMode RANDOM =
+ new LockDetectMode("RANDOM", DbConstants.DB_LOCK_RANDOM);
+
+ public static final LockDetectMode YOUNGEST =
+ new LockDetectMode("YOUNGEST", DbConstants.DB_LOCK_YOUNGEST);
+
+ /* package */
+ static LockDetectMode fromFlag(int flag) {
+ switch (flag) {
+ case 0:
+ return NONE;
+ case DbConstants.DB_LOCK_DEFAULT:
+ return DEFAULT;
+ case DbConstants.DB_LOCK_EXPIRE:
+ return EXPIRE;
+ case DbConstants.DB_LOCK_MAXLOCKS:
+ return MAXLOCKS;
+ case DbConstants.DB_LOCK_MAXWRITE:
+ return MAXWRITE;
+ case DbConstants.DB_LOCK_MINLOCKS:
+ return MINLOCKS;
+ case DbConstants.DB_LOCK_MINWRITE:
+ return MINWRITE;
+ case DbConstants.DB_LOCK_OLDEST:
+ return OLDEST;
+ case DbConstants.DB_LOCK_RANDOM:
+ return RANDOM;
+ case DbConstants.DB_LOCK_YOUNGEST:
+ return YOUNGEST;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown lock detect mode: " + flag);
+ }
+ }
+
+ private String modeName;
+ private int flag;
+
+ private LockDetectMode(final String modeName, final int flag) {
+ this.modeName = modeName;
+ this.flag = flag;
+ }
+
+ /* package */
+ int getFlag() {
+ return flag;
+ }
+
+ public String toString() {
+ return "LockDetectMode." + modeName;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LockMode.java b/db/java/src/com/sleepycat/db/LockMode.java
new file mode 100644
index 000000000..ab03d49a5
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LockMode.java
@@ -0,0 +1,40 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: LockMode.java,v 1.2 2004/04/09 15:08:38 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public final class LockMode {
+ private String lockModeName;
+ private int flag;
+
+ private LockMode(String lockModeName, int flag) {
+ this.lockModeName = lockModeName;
+ this.flag = flag;
+ }
+
+ public static final LockMode DEFAULT =
+ new LockMode("DEFAULT", 0);
+ public static final LockMode DIRTY_READ =
+ new LockMode("DIRTY_READ", DbConstants.DB_DIRTY_READ);
+ public static final LockMode DEGREE_2 =
+ new LockMode("DEGREE_2", DbConstants.DB_DEGREE_2);
+ public static final LockMode RMW =
+ new LockMode("RMW", DbConstants.DB_RMW);
+
+ public String toString() {
+ return "LockMode." + lockModeName;
+ }
+
+ /* package */
+ static int getFlag(LockMode mode) {
+ return ((mode == null) ? DEFAULT : mode).flag;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LockNotGrantedException.java b/db/java/src/com/sleepycat/db/LockNotGrantedException.java
new file mode 100644
index 000000000..a3f4c0b29
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LockNotGrantedException.java
@@ -0,0 +1,57 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: LockNotGrantedException.java,v 1.2 2004/09/28 19:30:37 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbEnv;
+import com.sleepycat.db.internal.DbLock;
+
+public class LockNotGrantedException extends DeadlockException {
+ private int index;
+ private Lock lock;
+ private int mode;
+ private DatabaseEntry obj;
+ private int op;
+
+ protected LockNotGrantedException(final String message,
+ final int op,
+ final int mode,
+ final DatabaseEntry obj,
+ final DbLock lock,
+ final int index,
+ final DbEnv dbenv) {
+ super(message, DbConstants.DB_LOCK_NOTGRANTED, dbenv);
+ this.op = op;
+ this.mode = mode;
+ this.obj = obj;
+ this.lock = lock.wrapper;
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public Lock getLock() {
+ return lock;
+ }
+
+ public int getMode() {
+ return mode;
+ }
+
+ public DatabaseEntry getObj() {
+ return obj;
+ }
+
+ public int getOp() {
+ return op;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LockOperation.java b/db/java/src/com/sleepycat/db/LockOperation.java
new file mode 100644
index 000000000..4bda73728
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LockOperation.java
@@ -0,0 +1,65 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: LockOperation.java,v 1.2 2004/04/21 01:09:09 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public final class LockOperation {
+ public static final LockOperation GET =
+ new LockOperation("GET", DbConstants.DB_LOCK_GET);
+ public static final LockOperation GET_TIMEOUT =
+ new LockOperation("GET_TIMEOUT", DbConstants.DB_LOCK_GET_TIMEOUT);
+ public static final LockOperation PUT =
+ new LockOperation("PUT", DbConstants.DB_LOCK_PUT);
+ public static final LockOperation PUT_ALL =
+ new LockOperation("PUT_ALL", DbConstants.DB_LOCK_PUT_ALL);
+ public static final LockOperation PUT_OBJ =
+ new LockOperation("PUT_OBJ", DbConstants.DB_LOCK_PUT_OBJ);
+ public static final LockOperation TIMEOUT =
+ new LockOperation("TIMEOUT", DbConstants.DB_LOCK_TIMEOUT);
+
+ /* package */
+ static LockOperation fromFlag(int flag) {
+ switch (flag) {
+ case DbConstants.DB_LOCK_GET:
+ return GET;
+ case DbConstants.DB_LOCK_GET_TIMEOUT:
+ return GET_TIMEOUT;
+ case DbConstants.DB_LOCK_PUT:
+ return PUT;
+ case DbConstants.DB_LOCK_PUT_ALL:
+ return PUT_ALL;
+ case DbConstants.DB_LOCK_PUT_OBJ:
+ return PUT_OBJ;
+ case DbConstants.DB_LOCK_TIMEOUT:
+ return TIMEOUT;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown lock operation: " + flag);
+ }
+ }
+
+ private final String operationName;
+ private final int flag;
+
+ private LockOperation(final String operationName, final int flag) {
+ this.operationName = operationName;
+ this.flag = flag;
+ }
+
+ public String toString() {
+ return "LockOperation." + operationName;
+ }
+
+ /* package */
+ int getFlag() {
+ return flag;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LockRequest.java b/db/java/src/com/sleepycat/db/LockRequest.java
new file mode 100644
index 000000000..d5d5a8fa3
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LockRequest.java
@@ -0,0 +1,83 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: LockRequest.java,v 1.3 2004/07/26 17:01:51 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbLock;
+
+public class LockRequest {
+ private DbLock lock;
+ private LockRequestMode mode;
+ private int modeFlag;
+ private DatabaseEntry obj;
+ private int op;
+ private int timeout;
+
+ public LockRequest(final LockOperation op,
+ final LockRequestMode mode,
+ final DatabaseEntry obj,
+ final Lock lock) {
+
+ this(op, mode, obj, lock, 0);
+ }
+
+ public LockRequest(final LockOperation op,
+ final LockRequestMode mode,
+ final DatabaseEntry obj,
+ final Lock lock,
+ final int timeout) {
+
+ this.setOp(op);
+ this.setMode(mode);
+ this.setObj(obj);
+ this.setLock(lock);
+ this.setTimeout(timeout);
+ }
+
+ public void setLock(final Lock lock) {
+ this.lock = lock.unwrap();
+ }
+
+ public void setMode(final LockRequestMode mode) {
+ this.mode = mode;
+ this.modeFlag = mode.getFlag();
+ }
+
+ public void setObj(final DatabaseEntry obj) {
+ this.obj = obj;
+ }
+
+ public void setOp(final LockOperation op) {
+ this.op = op.getFlag();
+ }
+
+ public void setTimeout(final int timeout) {
+ this.timeout = timeout;
+ }
+
+ public Lock getLock() {
+ return lock.wrapper;
+ }
+
+ public LockRequestMode getMode() {
+ return mode;
+ }
+
+ public DatabaseEntry getObj() {
+ return obj;
+ }
+
+ public LockOperation getOp() {
+ return LockOperation.fromFlag(op);
+ }
+
+ public int getTimeout() {
+ return timeout;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LockRequestMode.java b/db/java/src/com/sleepycat/db/LockRequestMode.java
new file mode 100644
index 000000000..14f49d5c6
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LockRequestMode.java
@@ -0,0 +1,43 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: LockRequestMode.java,v 1.2 2004/07/26 17:01:51 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public final class LockRequestMode {
+ public static final LockRequestMode READ =
+ new LockRequestMode("READ", DbConstants.DB_LOCK_READ);
+ public static final LockRequestMode WRITE =
+ new LockRequestMode("WRITE", DbConstants.DB_LOCK_WRITE);
+ public static final LockRequestMode IWRITE =
+ new LockRequestMode("IWRITE", DbConstants.DB_LOCK_IWRITE);
+ public static final LockRequestMode IREAD =
+ new LockRequestMode("IREAD", DbConstants.DB_LOCK_IREAD);
+ public static final LockRequestMode IWR =
+ new LockRequestMode("IWR", DbConstants.DB_LOCK_IWR);
+
+ /* package */
+ private final String operationName;
+ private final int flag;
+
+ public LockRequestMode(final String operationName, final int flag) {
+ this.operationName = operationName;
+ this.flag = flag;
+ }
+
+ public String toString() {
+ return "LockRequestMode." + operationName;
+ }
+
+ /* package */
+ int getFlag() {
+ return flag;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LockStats.java b/db/java/src/com/sleepycat/db/LockStats.java
new file mode 100644
index 000000000..a16a22616
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LockStats.java
@@ -0,0 +1,164 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class LockStats {
+ // no public constructor
+ protected LockStats() {}
+
+ private int st_id;
+ public int getId() {
+ return st_id;
+ }
+
+ private int st_cur_maxid;
+ public int getCurMaxId() {
+ return st_cur_maxid;
+ }
+
+ private int st_maxlocks;
+ public int getMaxLocks() {
+ return st_maxlocks;
+ }
+
+ private int st_maxlockers;
+ public int getMaxLockers() {
+ return st_maxlockers;
+ }
+
+ private int st_maxobjects;
+ public int getMaxObjects() {
+ return st_maxobjects;
+ }
+
+ private int st_nmodes;
+ public int getNumModes() {
+ return st_nmodes;
+ }
+
+ private int st_nlocks;
+ public int getNumLocks() {
+ return st_nlocks;
+ }
+
+ private int st_maxnlocks;
+ public int getMaxNlocks() {
+ return st_maxnlocks;
+ }
+
+ private int st_nlockers;
+ public int getNumLockers() {
+ return st_nlockers;
+ }
+
+ private int st_maxnlockers;
+ public int getMaxNlockers() {
+ return st_maxnlockers;
+ }
+
+ private int st_nobjects;
+ public int getNobjects() {
+ return st_nobjects;
+ }
+
+ private int st_maxnobjects;
+ public int getMaxNobjects() {
+ return st_maxnobjects;
+ }
+
+ private int st_nconflicts;
+ public int getNumConflicts() {
+ return st_nconflicts;
+ }
+
+ private int st_nrequests;
+ public int getNumRequests() {
+ return st_nrequests;
+ }
+
+ private int st_nreleases;
+ public int getNumReleases() {
+ return st_nreleases;
+ }
+
+ private int st_nnowaits;
+ public int getNumNowaits() {
+ return st_nnowaits;
+ }
+
+ private int st_ndeadlocks;
+ public int getNumDeadlocks() {
+ return st_ndeadlocks;
+ }
+
+ private int st_locktimeout;
+ public int getLockTimeout() {
+ return st_locktimeout;
+ }
+
+ private int st_nlocktimeouts;
+ public int getNumLockTimeouts() {
+ return st_nlocktimeouts;
+ }
+
+ private int st_txntimeout;
+ public int getTxnTimeout() {
+ return st_txntimeout;
+ }
+
+ private int st_ntxntimeouts;
+ public int getNumTxnTimeouts() {
+ return st_ntxntimeouts;
+ }
+
+ private int st_region_wait;
+ public int getRegionWait() {
+ return st_region_wait;
+ }
+
+ private int st_region_nowait;
+ public int getRegionNowait() {
+ return st_region_nowait;
+ }
+
+ private int st_regsize;
+ public int getRegSize() {
+ return st_regsize;
+ }
+
+ public String toString() {
+ return "LockStats:"
+ + "\n st_id=" + st_id
+ + "\n st_cur_maxid=" + st_cur_maxid
+ + "\n st_maxlocks=" + st_maxlocks
+ + "\n st_maxlockers=" + st_maxlockers
+ + "\n st_maxobjects=" + st_maxobjects
+ + "\n st_nmodes=" + st_nmodes
+ + "\n st_nlocks=" + st_nlocks
+ + "\n st_maxnlocks=" + st_maxnlocks
+ + "\n st_nlockers=" + st_nlockers
+ + "\n st_maxnlockers=" + st_maxnlockers
+ + "\n st_nobjects=" + st_nobjects
+ + "\n st_maxnobjects=" + st_maxnobjects
+ + "\n st_nconflicts=" + st_nconflicts
+ + "\n st_nrequests=" + st_nrequests
+ + "\n st_nreleases=" + st_nreleases
+ + "\n st_nnowaits=" + st_nnowaits
+ + "\n st_ndeadlocks=" + st_ndeadlocks
+ + "\n st_locktimeout=" + st_locktimeout
+ + "\n st_nlocktimeouts=" + st_nlocktimeouts
+ + "\n st_txntimeout=" + st_txntimeout
+ + "\n st_ntxntimeouts=" + st_ntxntimeouts
+ + "\n st_region_wait=" + st_region_wait
+ + "\n st_region_nowait=" + st_region_nowait
+ + "\n st_regsize=" + st_regsize
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LogCursor.java b/db/java/src/com/sleepycat/db/LogCursor.java
new file mode 100644
index 000000000..e15576ed9
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LogCursor.java
@@ -0,0 +1,80 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: LogCursor.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbLogc;
+
+public class LogCursor {
+ protected DbLogc logc;
+
+ protected LogCursor(final DbLogc logc) {
+ this.logc = logc;
+ }
+
+ /* package */
+ static LogCursor wrap(DbLogc logc) {
+ return (logc == null) ? null : new LogCursor(logc);
+ }
+
+ public synchronized void close()
+ throws DatabaseException {
+
+ logc.close(0);
+ }
+
+ public OperationStatus getCurrent(final LogSequenceNumber lsn,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ logc.get(lsn, data, DbConstants.DB_CURRENT));
+ }
+
+ public OperationStatus getNext(final LogSequenceNumber lsn,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ logc.get(lsn, data, DbConstants.DB_NEXT));
+ }
+
+ public OperationStatus getFirst(final LogSequenceNumber lsn,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ logc.get(lsn, data, DbConstants.DB_FIRST));
+ }
+
+ public OperationStatus getLast(final LogSequenceNumber lsn,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ logc.get(lsn, data, DbConstants.DB_LAST));
+ }
+
+ public OperationStatus getPrev(final LogSequenceNumber lsn,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ logc.get(lsn, data, DbConstants.DB_PREV));
+ }
+
+ public OperationStatus set(final LogSequenceNumber lsn,
+ final DatabaseEntry data)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ logc.get(lsn, data, DbConstants.DB_SET));
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LogRecordHandler.java b/db/java/src/com/sleepycat/db/LogRecordHandler.java
new file mode 100644
index 000000000..db4d6368c
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LogRecordHandler.java
@@ -0,0 +1,17 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: LogRecordHandler.java,v 1.2 2004/04/21 01:09:09 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public interface LogRecordHandler {
+ int handleLogRecord(Environment dbenv,
+ DatabaseEntry logRecord,
+ LogSequenceNumber lsn,
+ RecoveryOperation operation);
+}
diff --git a/db/java/src/com/sleepycat/db/LogSequenceNumber.java b/db/java/src/com/sleepycat/db/LogSequenceNumber.java
new file mode 100644
index 000000000..7d1d09313
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LogSequenceNumber.java
@@ -0,0 +1,38 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: LogSequenceNumber.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbEnv;
+
+public class LogSequenceNumber {
+ private int file;
+ private int offset;
+
+ public LogSequenceNumber(final int file, final int offset) {
+ this.file = file;
+ this.offset = offset;
+ }
+
+ public LogSequenceNumber() {
+ this(0, 0);
+ }
+
+ public int getFile() {
+ return file;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public static int compare(LogSequenceNumber lsn1, LogSequenceNumber lsn2) {
+ return DbEnv.log_compare(lsn1, lsn2);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/LogStats.java b/db/java/src/com/sleepycat/db/LogStats.java
new file mode 100644
index 000000000..aacf1e8f0
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/LogStats.java
@@ -0,0 +1,146 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class LogStats {
+ // no public constructor
+ protected LogStats() {}
+
+ private int st_magic;
+ public int getMagic() {
+ return st_magic;
+ }
+
+ private int st_version;
+ public int getVersion() {
+ return st_version;
+ }
+
+ private int st_mode;
+ public int getMode() {
+ return st_mode;
+ }
+
+ private int st_lg_bsize;
+ public int getLgBSize() {
+ return st_lg_bsize;
+ }
+
+ private int st_lg_size;
+ public int getLgSize() {
+ return st_lg_size;
+ }
+
+ private int st_w_bytes;
+ public int getWBytes() {
+ return st_w_bytes;
+ }
+
+ private int st_w_mbytes;
+ public int getWMbytes() {
+ return st_w_mbytes;
+ }
+
+ private int st_wc_bytes;
+ public int getWcBytes() {
+ return st_wc_bytes;
+ }
+
+ private int st_wc_mbytes;
+ public int getWcMbytes() {
+ return st_wc_mbytes;
+ }
+
+ private int st_wcount;
+ public int getWCount() {
+ return st_wcount;
+ }
+
+ private int st_wcount_fill;
+ public int getWCountFill() {
+ return st_wcount_fill;
+ }
+
+ private int st_scount;
+ public int getSCount() {
+ return st_scount;
+ }
+
+ private int st_region_wait;
+ public int getRegionWait() {
+ return st_region_wait;
+ }
+
+ private int st_region_nowait;
+ public int getRegionNowait() {
+ return st_region_nowait;
+ }
+
+ private int st_cur_file;
+ public int getCurFile() {
+ return st_cur_file;
+ }
+
+ private int st_cur_offset;
+ public int getCurOffset() {
+ return st_cur_offset;
+ }
+
+ private int st_disk_file;
+ public int getDiskFile() {
+ return st_disk_file;
+ }
+
+ private int st_disk_offset;
+ public int getDiskOffset() {
+ return st_disk_offset;
+ }
+
+ private int st_regsize;
+ public int getRegSize() {
+ return st_regsize;
+ }
+
+ private int st_maxcommitperflush;
+ public int getMaxCommitperflush() {
+ return st_maxcommitperflush;
+ }
+
+ private int st_mincommitperflush;
+ public int getMinCommitperflush() {
+ return st_mincommitperflush;
+ }
+
+ public String toString() {
+ return "LogStats:"
+ + "\n st_magic=" + st_magic
+ + "\n st_version=" + st_version
+ + "\n st_mode=" + st_mode
+ + "\n st_lg_bsize=" + st_lg_bsize
+ + "\n st_lg_size=" + st_lg_size
+ + "\n st_w_bytes=" + st_w_bytes
+ + "\n st_w_mbytes=" + st_w_mbytes
+ + "\n st_wc_bytes=" + st_wc_bytes
+ + "\n st_wc_mbytes=" + st_wc_mbytes
+ + "\n st_wcount=" + st_wcount
+ + "\n st_wcount_fill=" + st_wcount_fill
+ + "\n st_scount=" + st_scount
+ + "\n st_region_wait=" + st_region_wait
+ + "\n st_region_nowait=" + st_region_nowait
+ + "\n st_cur_file=" + st_cur_file
+ + "\n st_cur_offset=" + st_cur_offset
+ + "\n st_disk_file=" + st_disk_file
+ + "\n st_disk_offset=" + st_disk_offset
+ + "\n st_regsize=" + st_regsize
+ + "\n st_maxcommitperflush=" + st_maxcommitperflush
+ + "\n st_mincommitperflush=" + st_mincommitperflush
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/MemoryException.java b/db/java/src/com/sleepycat/db/MemoryException.java
new file mode 100644
index 000000000..7c612494f
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/MemoryException.java
@@ -0,0 +1,41 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: MemoryException.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbEnv;
+
+public class MemoryException extends DatabaseException {
+ private DatabaseEntry dbt = null;
+ private String message;
+
+ protected MemoryException(final String s,
+ final DatabaseEntry dbt,
+ final int errno,
+ final DbEnv dbenv) {
+ super(s, errno, dbenv);
+ this.message = s;
+ this.dbt = dbt;
+ }
+
+ public DatabaseEntry getDatabaseEntry() {
+ return dbt;
+ }
+
+ public String toString() {
+ return message;
+ }
+
+ void updateDatabaseEntry(final DatabaseEntry newEntry) {
+ if (this.dbt == null) {
+ this.message = "DatabaseEntry not large enough for available data";
+ this.dbt = newEntry;
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/MessageHandler.java b/db/java/src/com/sleepycat/db/MessageHandler.java
new file mode 100644
index 000000000..a28e35820
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/MessageHandler.java
@@ -0,0 +1,14 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: MessageHandler.java,v 1.2 2004/04/20 20:45:11 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public interface MessageHandler {
+ void message(Environment dbenv, String message);
+}
diff --git a/db/java/src/com/sleepycat/db/MultipleDataEntry.java b/db/java/src/com/sleepycat/db/MultipleDataEntry.java
new file mode 100644
index 000000000..17c2af473
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/MultipleDataEntry.java
@@ -0,0 +1,57 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: MultipleDataEntry.java,v 1.2 2004/04/09 15:08:38 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbUtil;
+
+public class MultipleDataEntry extends MultipleEntry {
+ public MultipleDataEntry() {
+ super(null, 0, 0);
+ }
+
+ public MultipleDataEntry(final byte[] data) {
+ super(data, 0, (data == null) ? 0 : data.length);
+ }
+
+ public MultipleDataEntry(final byte[] data,
+ final int offset,
+ final int size) {
+ super(data, offset, size);
+ }
+
+ /* package */
+ int getMultiFlag() {
+ return DbConstants.DB_MULTIPLE;
+ }
+
+ public boolean next(final DatabaseEntry data) {
+ if (pos == 0)
+ pos = ulen - INT32SZ;
+
+ final int dataoff = DbUtil.array2int(this.data, pos);
+
+ // crack out the data offset and length.
+ if (dataoff < 0) {
+ return (false);
+ }
+
+ pos -= INT32SZ;
+ final int datasz = DbUtil.array2int(this.data, pos);
+
+ pos -= INT32SZ;
+
+ data.setData(this.data);
+ data.setSize(datasz);
+ data.setOffset(dataoff);
+
+ return (true);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/MultipleEntry.java b/db/java/src/com/sleepycat/db/MultipleEntry.java
new file mode 100644
index 000000000..f3025a5e5
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/MultipleEntry.java
@@ -0,0 +1,28 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: MultipleEntry.java,v 1.4 2004/09/28 19:30:37 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public abstract class MultipleEntry extends DatabaseEntry {
+ protected int pos;
+
+ protected MultipleEntry(final byte[] data, final int offset, final int size) {
+ super(data, offset, size);
+ setUserBuffer(data.length - offset, true);
+ this.flags |= DbConstants.DB_DBT_USERMEM;
+ }
+
+ public void setUserBuffer(final int length, final boolean usermem) {
+ if (!usermem)
+ throw new IllegalArgumentException("User buffer required");
+ super.setUserBuffer(length, usermem);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/MultipleKeyDataEntry.java b/db/java/src/com/sleepycat/db/MultipleKeyDataEntry.java
new file mode 100644
index 000000000..17234b640
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/MultipleKeyDataEntry.java
@@ -0,0 +1,63 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: MultipleKeyDataEntry.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbUtil;
+
+public class MultipleKeyDataEntry extends MultipleEntry {
+ public MultipleKeyDataEntry() {
+ super(null, 0, 0);
+ }
+
+ public MultipleKeyDataEntry(final byte[] data) {
+ super(data, 0, (data == null) ? 0 : data.length);
+ }
+
+ public MultipleKeyDataEntry(final byte[] data,
+ final int offset,
+ final int size) {
+ super(data, offset, size);
+ }
+
+ /* package */
+ int getMultiFlag() {
+ return DbConstants.DB_MULTIPLE_KEY;
+ }
+
+ public boolean next(final DatabaseEntry key, final DatabaseEntry data) {
+ if (pos == 0)
+ pos = ulen - INT32SZ;
+
+ final int keyoff = DbUtil.array2int(this.data, pos);
+
+ // crack out the key and data offsets and lengths.
+ if (keyoff < 0)
+ return false;
+
+ pos -= INT32SZ;
+ final int keysz = DbUtil.array2int(this.data, pos);
+ pos -= INT32SZ;
+ final int dataoff = DbUtil.array2int(this.data, pos);
+ pos -= INT32SZ;
+ final int datasz = DbUtil.array2int(this.data, pos);
+ pos -= INT32SZ;
+
+ key.setData(this.data);
+ key.setOffset(keyoff);
+ key.setSize(keysz);
+
+ data.setData(this.data);
+ data.setOffset(dataoff);
+ data.setSize(datasz);
+
+ return true;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/MultipleRecnoDataEntry.java b/db/java/src/com/sleepycat/db/MultipleRecnoDataEntry.java
new file mode 100644
index 000000000..016c671d0
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/MultipleRecnoDataEntry.java
@@ -0,0 +1,61 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: MultipleRecnoDataEntry.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbUtil;
+
+public class MultipleRecnoDataEntry extends MultipleEntry {
+ public MultipleRecnoDataEntry() {
+ super(null, 0, 0);
+ }
+
+ public MultipleRecnoDataEntry(final byte[] data) {
+ super(data, 0, (data == null) ? 0 : data.length);
+ }
+
+ public MultipleRecnoDataEntry(final byte[] data,
+ final int offset,
+ final int size) {
+ super(data, offset, size);
+ }
+
+ /* package */
+ int getMultiFlag() {
+ return DbConstants.DB_MULTIPLE_KEY;
+ }
+
+ public boolean next(final DatabaseEntry recno, final DatabaseEntry data) {
+ if (pos == 0)
+ pos = ulen - INT32SZ;
+
+ final int keyoff = DbUtil.array2int(this.data, pos);
+
+ // crack out the key offset and the data offset and length.
+ if (keyoff < 0)
+ return false;
+
+ pos -= INT32SZ;
+ final int dataoff = DbUtil.array2int(this.data, pos);
+ pos -= INT32SZ;
+ final int datasz = DbUtil.array2int(this.data, pos);
+ pos -= INT32SZ;
+
+ recno.setData(this.data);
+ recno.setOffset(keyoff);
+ recno.setSize(INT32SZ);
+
+ data.setData(this.data);
+ data.setOffset(dataoff);
+ data.setSize(datasz);
+
+ return true;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/OperationStatus.java b/db/java/src/com/sleepycat/db/OperationStatus.java
new file mode 100644
index 000000000..5739bc6e2
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/OperationStatus.java
@@ -0,0 +1,54 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: OperationStatus.java,v 1.2 2004/04/21 01:09:09 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbEnv;
+
+public final class OperationStatus {
+ public static final OperationStatus SUCCESS =
+ new OperationStatus("SUCCESS", 0);
+ public static final OperationStatus KEYEXIST =
+ new OperationStatus("KEYEXIST", DbConstants.DB_KEYEXIST);
+ public static final OperationStatus KEYEMPTY =
+ new OperationStatus("KEYEMPTY", DbConstants.DB_KEYEMPTY);
+ public static final OperationStatus NOTFOUND =
+ new OperationStatus("NOTFOUND", DbConstants.DB_NOTFOUND);
+
+ /* package */
+ static OperationStatus fromInt(final int errCode) {
+ switch(errCode) {
+ case 0:
+ return SUCCESS;
+ case DbConstants.DB_KEYEXIST:
+ return KEYEXIST;
+ case DbConstants.DB_KEYEMPTY:
+ return KEYEMPTY;
+ case DbConstants.DB_NOTFOUND:
+ return NOTFOUND;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown error code: " + DbEnv.strerror(errCode));
+ }
+ }
+
+ /* For toString */
+ private String statusName;
+ private int errCode;
+
+ private OperationStatus(final String statusName, int errCode) {
+ this.statusName = statusName;
+ this.errCode = errCode;
+ }
+
+ public String toString() {
+ return "OperationStatus." + statusName;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/PanicHandler.java b/db/java/src/com/sleepycat/db/PanicHandler.java
new file mode 100644
index 000000000..7c9b838a8
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/PanicHandler.java
@@ -0,0 +1,14 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: PanicHandler.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public interface PanicHandler {
+ void panic(Environment dbenv, DatabaseException e);
+}
diff --git a/db/java/src/com/sleepycat/db/PreparedTransaction.java b/db/java/src/com/sleepycat/db/PreparedTransaction.java
new file mode 100644
index 000000000..36d103134
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/PreparedTransaction.java
@@ -0,0 +1,30 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: PreparedTransaction.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbTxn;
+
+public class PreparedTransaction {
+ private byte[] gid;
+ private Transaction txn;
+
+ PreparedTransaction(final DbTxn txn, final byte[] gid) {
+ this.txn = new Transaction(txn);
+ this.gid = gid;
+ }
+
+ public byte[] getGID() {
+ return gid;
+ }
+
+ public Transaction getTransaction() {
+ return txn;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/QueueStats.java b/db/java/src/com/sleepycat/db/QueueStats.java
new file mode 100644
index 000000000..10ad3f768
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/QueueStats.java
@@ -0,0 +1,98 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class QueueStats extends DatabaseStats {
+ // no public constructor
+ protected QueueStats() {}
+
+ private int qs_magic;
+ public int getMagic() {
+ return qs_magic;
+ }
+
+ private int qs_version;
+ public int getVersion() {
+ return qs_version;
+ }
+
+ private int qs_metaflags;
+ public int getMetaFlags() {
+ return qs_metaflags;
+ }
+
+ private int qs_nkeys;
+ public int getNumKeys() {
+ return qs_nkeys;
+ }
+
+ private int qs_ndata;
+ public int getNumData() {
+ return qs_ndata;
+ }
+
+ private int qs_pagesize;
+ public int getPageSize() {
+ return qs_pagesize;
+ }
+
+ private int qs_extentsize;
+ public int getExtentSize() {
+ return qs_extentsize;
+ }
+
+ private int qs_pages;
+ public int getPages() {
+ return qs_pages;
+ }
+
+ private int qs_re_len;
+ public int getReLen() {
+ return qs_re_len;
+ }
+
+ private int qs_re_pad;
+ public int getRePad() {
+ return qs_re_pad;
+ }
+
+ private int qs_pgfree;
+ public int getPagesFree() {
+ return qs_pgfree;
+ }
+
+ private int qs_first_recno;
+ public int getFirstRecno() {
+ return qs_first_recno;
+ }
+
+ private int qs_cur_recno;
+ public int getCurRecno() {
+ return qs_cur_recno;
+ }
+
+ public String toString() {
+ return "QueueStats:"
+ + "\n qs_magic=" + qs_magic
+ + "\n qs_version=" + qs_version
+ + "\n qs_metaflags=" + qs_metaflags
+ + "\n qs_nkeys=" + qs_nkeys
+ + "\n qs_ndata=" + qs_ndata
+ + "\n qs_pagesize=" + qs_pagesize
+ + "\n qs_extentsize=" + qs_extentsize
+ + "\n qs_pages=" + qs_pages
+ + "\n qs_re_len=" + qs_re_len
+ + "\n qs_re_pad=" + qs_re_pad
+ + "\n qs_pgfree=" + qs_pgfree
+ + "\n qs_first_recno=" + qs_first_recno
+ + "\n qs_cur_recno=" + qs_cur_recno
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/RecordNumberAppender.java b/db/java/src/com/sleepycat/db/RecordNumberAppender.java
new file mode 100644
index 000000000..4162a1ead
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/RecordNumberAppender.java
@@ -0,0 +1,15 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: RecordNumberAppender.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db;
+
+public interface RecordNumberAppender {
+ void appendRecordNumber(Database db, DatabaseEntry data, int recno)
+ throws DatabaseException;
+}
diff --git a/db/java/src/com/sleepycat/db/RecoveryOperation.java b/db/java/src/com/sleepycat/db/RecoveryOperation.java
new file mode 100644
index 000000000..938eacd2e
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/RecoveryOperation.java
@@ -0,0 +1,56 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: RecoveryOperation.java,v 1.1 2004/04/21 01:09:09 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public final class RecoveryOperation {
+ public static final RecoveryOperation BACKWARD_ROLL =
+ new RecoveryOperation("BACKWARD_ROLL", DbConstants.DB_TXN_BACKWARD_ROLL);
+ public static final RecoveryOperation FORWARD_ROLL =
+ new RecoveryOperation("FORWARD_ROLL", DbConstants.DB_TXN_FORWARD_ROLL);
+ public static final RecoveryOperation ABORT =
+ new RecoveryOperation("ABORT", DbConstants.DB_TXN_ABORT);
+ public static final RecoveryOperation APPLY =
+ new RecoveryOperation("APPLY", DbConstants.DB_TXN_APPLY);
+ public static final RecoveryOperation PRINT =
+ new RecoveryOperation("PRINT", DbConstants.DB_TXN_PRINT);
+
+ private String operationName;
+ private int flag;
+
+ private RecoveryOperation(String operationName, int flag) {
+ this.operationName = operationName;
+ this.flag = flag;
+ }
+
+ public String toString() {
+ return "RecoveryOperation." + operationName;
+ }
+
+ /* This is public only so it can be called from internal/DbEnv.java. */
+ public static RecoveryOperation fromFlag(int flag) {
+ switch (flag) {
+ case DbConstants.DB_TXN_BACKWARD_ROLL:
+ return BACKWARD_ROLL;
+ case DbConstants.DB_TXN_FORWARD_ROLL:
+ return FORWARD_ROLL;
+ case DbConstants.DB_TXN_ABORT:
+ return ABORT;
+ case DbConstants.DB_TXN_APPLY:
+ return APPLY;
+ case DbConstants.DB_TXN_PRINT:
+ return PRINT;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown recover operation: " + flag);
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/ReplicationHandleDeadException.java b/db/java/src/com/sleepycat/db/ReplicationHandleDeadException.java
new file mode 100644
index 000000000..cfcf92ab6
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/ReplicationHandleDeadException.java
@@ -0,0 +1,20 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ReplicationHandleDeadException.java,v 1.1 2004/09/23 17:56:39 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbEnv;
+
+public class ReplicationHandleDeadException extends DatabaseException {
+ protected ReplicationHandleDeadException(final String s,
+ final int errno,
+ final DbEnv dbenv) {
+ super(s, errno, dbenv);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/ReplicationStats.java b/db/java/src/com/sleepycat/db/ReplicationStats.java
new file mode 100644
index 000000000..70c2bf032
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/ReplicationStats.java
@@ -0,0 +1,278 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class ReplicationStats {
+ // no public constructor
+ protected ReplicationStats() {}
+
+ private int st_status;
+ public int getStatus() {
+ return st_status;
+ }
+
+ private LogSequenceNumber st_next_lsn;
+ public LogSequenceNumber getNextLsn() {
+ return st_next_lsn;
+ }
+
+ private LogSequenceNumber st_waiting_lsn;
+ public LogSequenceNumber getWaitingLsn() {
+ return st_waiting_lsn;
+ }
+
+ private int st_next_pg;
+ public int getNextPages() {
+ return st_next_pg;
+ }
+
+ private int st_waiting_pg;
+ public int getWaitingPages() {
+ return st_waiting_pg;
+ }
+
+ private int st_dupmasters;
+ public int getDupmasters() {
+ return st_dupmasters;
+ }
+
+ private int st_env_id;
+ public int getEnvId() {
+ return st_env_id;
+ }
+
+ private int st_env_priority;
+ public int getEnvPriority() {
+ return st_env_priority;
+ }
+
+ private int st_gen;
+ public int getGen() {
+ return st_gen;
+ }
+
+ private int st_egen;
+ public int getEgen() {
+ return st_egen;
+ }
+
+ private int st_log_duplicated;
+ public int getLogDuplicated() {
+ return st_log_duplicated;
+ }
+
+ private int st_log_queued;
+ public int getLogQueued() {
+ return st_log_queued;
+ }
+
+ private int st_log_queued_max;
+ public int getLogQueuedMax() {
+ return st_log_queued_max;
+ }
+
+ private int st_log_queued_total;
+ public int getLogQueuedTotal() {
+ return st_log_queued_total;
+ }
+
+ private int st_log_records;
+ public int getLogRecords() {
+ return st_log_records;
+ }
+
+ private int st_log_requested;
+ public int getLogRequested() {
+ return st_log_requested;
+ }
+
+ private int st_master;
+ public int getMaster() {
+ return st_master;
+ }
+
+ private int st_master_changes;
+ public int getMasterChanges() {
+ return st_master_changes;
+ }
+
+ private int st_msgs_badgen;
+ public int getMsgsBadgen() {
+ return st_msgs_badgen;
+ }
+
+ private int st_msgs_processed;
+ public int getMsgsProcessed() {
+ return st_msgs_processed;
+ }
+
+ private int st_msgs_recover;
+ public int getMsgsRecover() {
+ return st_msgs_recover;
+ }
+
+ private int st_msgs_send_failures;
+ public int getMsgsSendFailures() {
+ return st_msgs_send_failures;
+ }
+
+ private int st_msgs_sent;
+ public int getMsgsSent() {
+ return st_msgs_sent;
+ }
+
+ private int st_newsites;
+ public int getNewsites() {
+ return st_newsites;
+ }
+
+ private int st_nsites;
+ public int getNumSites() {
+ return st_nsites;
+ }
+
+ private int st_nthrottles;
+ public int getNumThrottles() {
+ return st_nthrottles;
+ }
+
+ private int st_outdated;
+ public int getOutdated() {
+ return st_outdated;
+ }
+
+ private int st_pg_duplicated;
+ public int getPagesDuplicated() {
+ return st_pg_duplicated;
+ }
+
+ private int st_pg_records;
+ public int getPagesRecords() {
+ return st_pg_records;
+ }
+
+ private int st_pg_requested;
+ public int getPagesRequested() {
+ return st_pg_requested;
+ }
+
+ private int st_startup_complete;
+ public int getStartupComplete() {
+ return st_startup_complete;
+ }
+
+ private int st_txns_applied;
+ public int getTxnsApplied() {
+ return st_txns_applied;
+ }
+
+ private int st_elections;
+ public int getElections() {
+ return st_elections;
+ }
+
+ private int st_elections_won;
+ public int getElectionsWon() {
+ return st_elections_won;
+ }
+
+ private int st_election_cur_winner;
+ public int getElectionCurWinner() {
+ return st_election_cur_winner;
+ }
+
+ private int st_election_gen;
+ public int getElectionGen() {
+ return st_election_gen;
+ }
+
+ private LogSequenceNumber st_election_lsn;
+ public LogSequenceNumber getElectionLsn() {
+ return st_election_lsn;
+ }
+
+ private int st_election_nsites;
+ public int getElectionNumSites() {
+ return st_election_nsites;
+ }
+
+ private int st_election_nvotes;
+ public int getElectionNumVotes() {
+ return st_election_nvotes;
+ }
+
+ private int st_election_priority;
+ public int getElectionPriority() {
+ return st_election_priority;
+ }
+
+ private int st_election_status;
+ public int getElectionStatus() {
+ return st_election_status;
+ }
+
+ private int st_election_tiebreaker;
+ public int getElectionTiebreaker() {
+ return st_election_tiebreaker;
+ }
+
+ private int st_election_votes;
+ public int getElectionVotes() {
+ return st_election_votes;
+ }
+
+ public String toString() {
+ return "ReplicationStats:"
+ + "\n st_status=" + st_status
+ + "\n st_next_lsn=" + st_next_lsn
+ + "\n st_waiting_lsn=" + st_waiting_lsn
+ + "\n st_next_pg=" + st_next_pg
+ + "\n st_waiting_pg=" + st_waiting_pg
+ + "\n st_dupmasters=" + st_dupmasters
+ + "\n st_env_id=" + st_env_id
+ + "\n st_env_priority=" + st_env_priority
+ + "\n st_gen=" + st_gen
+ + "\n st_egen=" + st_egen
+ + "\n st_log_duplicated=" + st_log_duplicated
+ + "\n st_log_queued=" + st_log_queued
+ + "\n st_log_queued_max=" + st_log_queued_max
+ + "\n st_log_queued_total=" + st_log_queued_total
+ + "\n st_log_records=" + st_log_records
+ + "\n st_log_requested=" + st_log_requested
+ + "\n st_master=" + st_master
+ + "\n st_master_changes=" + st_master_changes
+ + "\n st_msgs_badgen=" + st_msgs_badgen
+ + "\n st_msgs_processed=" + st_msgs_processed
+ + "\n st_msgs_recover=" + st_msgs_recover
+ + "\n st_msgs_send_failures=" + st_msgs_send_failures
+ + "\n st_msgs_sent=" + st_msgs_sent
+ + "\n st_newsites=" + st_newsites
+ + "\n st_nsites=" + st_nsites
+ + "\n st_nthrottles=" + st_nthrottles
+ + "\n st_outdated=" + st_outdated
+ + "\n st_pg_duplicated=" + st_pg_duplicated
+ + "\n st_pg_records=" + st_pg_records
+ + "\n st_pg_requested=" + st_pg_requested
+ + "\n st_startup_complete=" + st_startup_complete
+ + "\n st_txns_applied=" + st_txns_applied
+ + "\n st_elections=" + st_elections
+ + "\n st_elections_won=" + st_elections_won
+ + "\n st_election_cur_winner=" + st_election_cur_winner
+ + "\n st_election_gen=" + st_election_gen
+ + "\n st_election_lsn=" + st_election_lsn
+ + "\n st_election_nsites=" + st_election_nsites
+ + "\n st_election_nvotes=" + st_election_nvotes
+ + "\n st_election_priority=" + st_election_priority
+ + "\n st_election_status=" + st_election_status
+ + "\n st_election_tiebreaker=" + st_election_tiebreaker
+ + "\n st_election_votes=" + st_election_votes
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/ReplicationStatus.java b/db/java/src/com/sleepycat/db/ReplicationStatus.java
new file mode 100644
index 000000000..64e4fd2c1
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/ReplicationStatus.java
@@ -0,0 +1,121 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: ReplicationStatus.java,v 1.5 2004/08/17 20:04:42 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbEnv;
+
+public final class ReplicationStatus {
+ static final ReplicationStatus SUCCESS =
+ new ReplicationStatus("SUCCESS", 0);
+
+ private int errCode;
+ private DatabaseEntry cdata;
+ private int envid;
+ private LogSequenceNumber lsn;
+
+ /* For toString */
+ private String statusName;
+
+ private ReplicationStatus(final String statusName,
+ final int errCode,
+ final DatabaseEntry cdata,
+ final int envid,
+ final LogSequenceNumber lsn) {
+ this.statusName = statusName;
+ this.errCode = errCode;
+ this.cdata = cdata;
+ this.envid = envid;
+ this.lsn = lsn;
+ }
+
+ private ReplicationStatus(final String statusName, final int errCode) {
+ this(statusName, errCode, null, 0, null);
+ }
+
+ public boolean isSuccess() {
+ return errCode == 0;
+ }
+
+ public boolean isDupMaster() {
+ return errCode == DbConstants.DB_REP_DUPMASTER;
+ }
+
+ public boolean isHoldElection() {
+ return errCode == DbConstants.DB_REP_HOLDELECTION;
+ }
+
+ public boolean isPermanent() {
+ return errCode == DbConstants.DB_REP_ISPERM;
+ }
+
+ public boolean isNewMaster() {
+ return errCode == DbConstants.DB_REP_NEWMASTER;
+ }
+
+ public boolean isNewSite() {
+ return errCode == DbConstants.DB_REP_NEWSITE;
+ }
+
+ public boolean isNotPermanent() {
+ return errCode == DbConstants.DB_REP_NOTPERM;
+ }
+
+ public boolean isStartupDone() {
+ return errCode == DbConstants.DB_REP_STARTUPDONE;
+ }
+
+ public DatabaseEntry getCData() {
+ return cdata;
+ }
+
+ public int getEnvID() {
+ return envid;
+ }
+
+ public LogSequenceNumber getLSN() {
+ return lsn;
+ }
+
+ public String toString() {
+ return "ReplicationStatus." + statusName;
+ }
+
+ /* package */
+ static ReplicationStatus getStatus(final int errCode,
+ final DatabaseEntry cdata,
+ final int envid,
+ final LogSequenceNumber lsn) {
+ switch(errCode) {
+ case 0:
+ return SUCCESS;
+ case DbConstants.DB_REP_DUPMASTER:
+ return DUPMASTER;
+ case DbConstants.DB_REP_HOLDELECTION:
+ return HOLDELECTION;
+ case DbConstants.DB_REP_ISPERM:
+ return new ReplicationStatus("ISPERM", errCode, cdata, envid, lsn);
+ case DbConstants.DB_REP_NEWMASTER:
+ return new ReplicationStatus("NEWMASTER", errCode, cdata, envid, lsn);
+ case DbConstants.DB_REP_NEWSITE:
+ return new ReplicationStatus("NEWSITE", errCode, cdata, envid, lsn);
+ case DbConstants.DB_REP_NOTPERM:
+ return new ReplicationStatus("NOTPERM", errCode, cdata, envid, lsn);
+ default:
+ throw new IllegalArgumentException(
+ "Unknown error code: " + DbEnv.strerror(errCode));
+ }
+ }
+
+ private static final ReplicationStatus DUPMASTER =
+ new ReplicationStatus("DUPMASTER", DbConstants.DB_REP_DUPMASTER);
+ private static final ReplicationStatus HOLDELECTION =
+ new ReplicationStatus("HOLDELECTION", DbConstants.DB_REP_HOLDELECTION);
+}
diff --git a/db/java/src/com/sleepycat/db/ReplicationTransport.java b/db/java/src/com/sleepycat/db/ReplicationTransport.java
new file mode 100644
index 000000000..72b28a73c
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/ReplicationTransport.java
@@ -0,0 +1,26 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ReplicationTransport.java,v 1.3 2004/07/06 15:06:37 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public interface ReplicationTransport {
+ int send(Environment dbenv,
+ DatabaseEntry control,
+ DatabaseEntry rec,
+ LogSequenceNumber lsn,
+ int envid,
+ boolean noBuffer,
+ boolean permanent)
+ throws DatabaseException;
+
+ int EID_BROADCAST = DbConstants.DB_EID_BROADCAST;
+ int EID_INVALID = DbConstants.DB_EID_INVALID;
+}
diff --git a/db/java/src/com/sleepycat/db/RunRecoveryException.java b/db/java/src/com/sleepycat/db/RunRecoveryException.java
new file mode 100644
index 000000000..640d81af8
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/RunRecoveryException.java
@@ -0,0 +1,20 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: RunRecoveryException.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbEnv;
+
+public class RunRecoveryException extends DatabaseException {
+ protected RunRecoveryException(final String s,
+ final int errno,
+ final DbEnv dbenv) {
+ super(s, errno, dbenv);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/SecondaryConfig.java b/db/java/src/com/sleepycat/db/SecondaryConfig.java
new file mode 100644
index 000000000..e275e19e5
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/SecondaryConfig.java
@@ -0,0 +1,91 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: SecondaryConfig.java,v 1.3 2004/08/06 21:56:40 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.Db;
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbEnv;
+import com.sleepycat.db.internal.DbTxn;
+
+public class SecondaryConfig extends DatabaseConfig implements Cloneable {
+ /*
+ * For internal use, to allow null as a valid value for
+ * the config parameter.
+ */
+ public static final SecondaryConfig DEFAULT = new SecondaryConfig();
+
+ /* package */
+ static SecondaryConfig checkNull(SecondaryConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ private boolean allowPopulate;
+ private SecondaryKeyCreator keyCreator;
+
+ public SecondaryConfig() {
+ }
+
+ public void setKeyCreator(final SecondaryKeyCreator keyCreator) {
+ this.keyCreator = keyCreator;
+ }
+
+ public SecondaryKeyCreator getKeyCreator() {
+ return keyCreator;
+ }
+
+ public void setAllowPopulate(final boolean allowPopulate) {
+ this.allowPopulate = allowPopulate;
+ }
+
+ public boolean getAllowPopulate() {
+ return allowPopulate;
+ }
+
+ /* package */
+ Db openSecondaryDatabase(final DbEnv dbenv,
+ final DbTxn txn,
+ final String fileName,
+ final String databaseName,
+ final Db primary)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ int associateFlags = 0;
+ associateFlags |= allowPopulate ? DbConstants.DB_CREATE : 0;
+ if (getTransactional() && txn == null)
+ associateFlags |= DbConstants.DB_AUTO_COMMIT;
+
+ final Db db = super.openDatabase(dbenv, txn, fileName, databaseName);
+ boolean succeeded = false;
+ try {
+ primary.associate(txn, db, keyCreator, associateFlags);
+ succeeded = true;
+ return db;
+ } finally {
+ if (!succeeded)
+ try {
+ db.close(0);
+ } catch (Throwable t) {
+ // Ignore it -- there is already an exception in flight.
+ }
+ }
+ }
+
+ /* package */
+ SecondaryConfig(final Db db)
+ throws DatabaseException {
+
+ super(db);
+
+ // XXX: There is no way to find out whether allowPopulate was set.
+ allowPopulate = false;
+ keyCreator = db.get_seckey_create();
+ }
+}
+
diff --git a/db/java/src/com/sleepycat/db/SecondaryCursor.java b/db/java/src/com/sleepycat/db/SecondaryCursor.java
new file mode 100644
index 000000000..886ea5f92
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/SecondaryCursor.java
@@ -0,0 +1,250 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: SecondaryCursor.java,v 1.3 2004/04/21 01:09:09 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.Dbc;
+
+public class SecondaryCursor extends Cursor {
+ /* package */
+ SecondaryCursor(final SecondaryDatabase database,
+ final Dbc dbc,
+ final CursorConfig config)
+ throws DatabaseException {
+
+ super(database, dbc, config);
+ }
+
+ public SecondaryDatabase getSecondaryDatabase() {
+ return (SecondaryDatabase)super.getDatabase();
+ }
+
+ public Cursor dup(final boolean samePosition)
+ throws DatabaseException {
+
+ return dupSecondary(samePosition);
+ }
+
+ public SecondaryCursor dupSecondary(final boolean samePosition)
+ throws DatabaseException {
+
+ return new SecondaryCursor(getSecondaryDatabase(),
+ dbc.dup(samePosition ? DbConstants.DB_POSITION : 0), config);
+ }
+
+ public OperationStatus getCurrent(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_CURRENT | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getFirst(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_FIRST | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getLast(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_LAST | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getNext(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_NEXT | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getNextDup(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_NEXT_DUP | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getNextNoDup(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_NEXT_NODUP | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getPrev(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_PREV | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getPrevDup(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ /*
+ * "Get the previous duplicate" isn't directly supported by the C API,
+ * so here's how to get it: dup the cursor and call getPrev, then dup
+ * the result and call getNextDup. If both succeed then there was a
+ * previous duplicate and the first dup is sitting on it. Keep that,
+ * and call getCurrent to fill in the user's buffers.
+ */
+ Dbc dup1 = dbc.dup(DbConstants.DB_POSITION);
+ try {
+ int errCode = dup1.get(DatabaseEntry.IGNORE, DatabaseEntry.IGNORE,
+ DbConstants.DB_PREV | LockMode.getFlag(lockMode));
+ if (errCode == 0) {
+ Dbc dup2 = dup1.dup(DbConstants.DB_POSITION);
+ try {
+ errCode = dup2.get(DatabaseEntry.IGNORE,
+ DatabaseEntry.IGNORE,
+ DbConstants.DB_NEXT_DUP | LockMode.getFlag(lockMode));
+ } finally {
+ dup2.close();
+ }
+ }
+ if (errCode == 0)
+ errCode = dup1.pget(key, pKey, data,
+ DbConstants.DB_CURRENT | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag()));
+ if (errCode == 0) {
+ Dbc tdbc = dbc;
+ dbc = dup1;
+ dup1 = tdbc;
+ }
+ return OperationStatus.fromInt(errCode);
+ } finally {
+ dup1.close();
+ }
+ }
+
+ public OperationStatus getPrevNoDup(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_PREV_NODUP | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getRecordNumber(final DatabaseEntry secondaryRecno,
+ final DatabaseEntry primaryRecno,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(DatabaseEntry.IGNORE, secondaryRecno, primaryRecno,
+ DbConstants.DB_GET_RECNO | LockMode.getFlag(lockMode)));
+ }
+
+ public OperationStatus getSearchKey(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_SET | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchKeyRange(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_SET_RANGE | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchBoth(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_GET_BOTH | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchBothRange(final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(key, pKey, data,
+ DbConstants.DB_GET_BOTH_RANGE | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchRecordNumber(
+ final DatabaseEntry secondaryRecno,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ dbc.pget(secondaryRecno, pKey, data,
+ DbConstants.DB_SET_RECNO | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/SecondaryDatabase.java b/db/java/src/com/sleepycat/db/SecondaryDatabase.java
new file mode 100644
index 000000000..3ea7c1398
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/SecondaryDatabase.java
@@ -0,0 +1,106 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: SecondaryDatabase.java,v 1.3 2004/04/21 01:09:10 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.Db;
+import com.sleepycat.db.internal.DbConstants;
+
+public class SecondaryDatabase extends Database {
+ private final Database primaryDatabase;
+
+ /* package */
+ SecondaryDatabase(final Db db, final Database primaryDatabase)
+ throws DatabaseException {
+
+ super(db);
+ this.primaryDatabase = primaryDatabase;
+ }
+
+ public SecondaryDatabase(final String fileName,
+ final String databaseName,
+ final Database primaryDatabase,
+ final SecondaryConfig config)
+ throws DatabaseException, java.io.FileNotFoundException {
+
+ this(SecondaryConfig.checkNull(config).openSecondaryDatabase(
+ null, null, fileName, databaseName, primaryDatabase.db),
+ primaryDatabase);
+ }
+
+ public Cursor openCursor(final Transaction txn, final CursorConfig config)
+ throws DatabaseException {
+
+ return openSecondaryCursor(txn, config);
+ }
+
+ public SecondaryCursor openSecondaryCursor(final Transaction txn,
+ final CursorConfig config)
+ throws DatabaseException {
+
+ return new SecondaryCursor(this,
+ CursorConfig.checkNull(config).openCursor(db,
+ (txn == null) ? null : txn.txn), config);
+ }
+
+ public Database getPrimaryDatabase() {
+ return primaryDatabase;
+ }
+
+ public DatabaseConfig getConfig()
+ throws DatabaseException {
+
+ return getSecondaryConfig();
+ }
+
+ public SecondaryConfig getSecondaryConfig()
+ throws DatabaseException {
+
+ return new SecondaryConfig(db);
+ }
+
+ public OperationStatus get(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ final LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.pget((txn == null) ? null : txn.txn, key, pKey, data,
+ LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchBoth(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ final LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.pget((txn == null) ? null : txn.txn, key, pKey, data,
+ DbConstants.DB_GET_BOTH | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+
+ public OperationStatus getSearchRecordNumber(final Transaction txn,
+ final DatabaseEntry key,
+ final DatabaseEntry pKey,
+ final DatabaseEntry data,
+ final LockMode lockMode)
+ throws DatabaseException {
+
+ return OperationStatus.fromInt(
+ db.pget((txn == null) ? null : txn.txn, key, pKey, data,
+ DbConstants.DB_SET_RECNO | LockMode.getFlag(lockMode) |
+ ((data == null) ? 0 : data.getMultiFlag())));
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/SecondaryKeyCreator.java b/db/java/src/com/sleepycat/db/SecondaryKeyCreator.java
new file mode 100644
index 000000000..10d17c56e
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/SecondaryKeyCreator.java
@@ -0,0 +1,18 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: SecondaryKeyCreator.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+public interface SecondaryKeyCreator {
+ boolean createSecondaryKey(SecondaryDatabase secondary,
+ DatabaseEntry key,
+ DatabaseEntry data,
+ DatabaseEntry result)
+ throws DatabaseException;
+}
diff --git a/db/java/src/com/sleepycat/db/Sequence.java b/db/java/src/com/sleepycat/db/Sequence.java
new file mode 100644
index 000000000..14a6d29dd
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/Sequence.java
@@ -0,0 +1,63 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: Sequence.java,v 1.2 2004/09/28 19:30:37 mjc Exp $
+ */
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbSequence;
+
+public class Sequence {
+ private DbSequence seq;
+ private int autoCommitFlag;
+
+ /* package */
+ Sequence(final DbSequence seq, SequenceConfig config)
+ throws DatabaseException {
+
+ this.seq = seq;
+ seq.wrapper = this;
+ if (seq.get_db().get_transactional())
+ this.autoCommitFlag = DbConstants.DB_AUTO_COMMIT |
+ (SequenceConfig.checkNull(config).getAutoCommitNoSync() ?
+ DbConstants.DB_TXN_NOSYNC : 0);
+ }
+
+ public void close()
+ throws DatabaseException {
+
+ seq.close(0);
+ }
+
+ public long get(Transaction txn, int delta)
+ throws DatabaseException {
+
+ return seq.get((txn == null) ? null : txn.txn, delta,
+ (txn == null) ? autoCommitFlag : 0);
+ }
+
+ public Database getDatabase()
+ throws DatabaseException {
+
+ return seq.get_db().wrapper;
+ }
+
+ public DatabaseEntry getKey()
+ throws DatabaseException {
+
+ DatabaseEntry key = new DatabaseEntry();
+ seq.get_key(key);
+ return key;
+ }
+
+ public SequenceStats getStats(StatsConfig config)
+ throws DatabaseException {
+
+ return seq.stat(config.getFlags());
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/SequenceConfig.java b/db/java/src/com/sleepycat/db/SequenceConfig.java
new file mode 100644
index 000000000..2901ac1e6
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/SequenceConfig.java
@@ -0,0 +1,199 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: SequenceConfig.java,v 1.2 2004/09/23 17:56:39 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.Db;
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbSequence;
+import com.sleepycat.db.internal.DbTxn;
+
+public class SequenceConfig implements Cloneable {
+ /*
+ * For internal use, final to allow null as a valid value for
+ * the config parameter.
+ */
+ public static final SequenceConfig DEFAULT = new SequenceConfig();
+
+ /* package */
+ static SequenceConfig checkNull(SequenceConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ /* Parameters */
+ private int cacheSize = 0;
+ private long rangeMin = Long.MIN_VALUE;
+ private long rangeMax = Long.MAX_VALUE;
+ private long initialValue = 0L;
+
+ /* Flags */
+ private boolean allowCreate = false;
+ private boolean decrement = false;
+ private boolean exclusiveCreate = false;
+ private boolean autoCommitNoSync = false;
+ private boolean wrap = false;
+
+ public SequenceConfig() {
+ }
+
+ public void setAllowCreate(final boolean allowCreate) {
+ this.allowCreate = allowCreate;
+ }
+
+ public boolean getAllowCreate() {
+ return allowCreate;
+ }
+
+ public void setCacheSize(final int cacheSize) {
+ this.cacheSize = cacheSize;
+ }
+
+ public int getCacheSize() {
+ return cacheSize;
+ }
+
+ public void setDecrement(boolean decrement) {
+ this.decrement = decrement;
+ }
+
+ public boolean getDecrement() {
+ return decrement;
+ }
+
+ public void setExclusiveCreate(final boolean exclusiveCreate) {
+ this.exclusiveCreate = exclusiveCreate;
+ }
+
+ public boolean getExclusiveCreate() {
+ return exclusiveCreate;
+ }
+
+ public void setInitialValue(long initialValue) {
+ this.initialValue = initialValue;
+ }
+
+ public long getInitialValue() {
+ return initialValue;
+ }
+
+ public void setAutoCommitNoSync(final boolean autoCommitNoSync) {
+ this.autoCommitNoSync = autoCommitNoSync;
+ }
+
+ public boolean getAutoCommitNoSync() {
+ return autoCommitNoSync;
+ }
+
+ public void setRange(final long min, final long max) {
+ this.rangeMin = min;
+ this.rangeMax = max;
+ }
+
+ public long getRangeMin() {
+ return rangeMin;
+ }
+
+ public long getRangeMax() {
+ return rangeMax;
+ }
+
+ public void setWrap(final boolean wrap) {
+ this.wrap = wrap;
+ }
+
+ public boolean getWrap() {
+ return wrap;
+ }
+
+ /* package */
+ DbSequence createSequence(final Db db)
+ throws DatabaseException {
+
+ int createFlags = 0;
+
+ return new DbSequence(db, createFlags);
+ }
+
+ /* package */
+ DbSequence openSequence(final Db db,
+ final DbTxn txn,
+ final DatabaseEntry key)
+ throws DatabaseException {
+
+ final DbSequence seq = createSequence(db);
+ // The DB_THREAD flag is inherited from the database
+ boolean threaded = ((db.get_open_flags() & DbConstants.DB_THREAD) != 0);
+
+ int openFlags = 0;
+ openFlags |= allowCreate ? DbConstants.DB_CREATE : 0;
+ openFlags |= exclusiveCreate ? DbConstants.DB_EXCL : 0;
+ openFlags |= threaded ? DbConstants.DB_THREAD : 0;
+
+ if (db.get_transactional() && txn == null)
+ openFlags |= DbConstants.DB_AUTO_COMMIT;
+
+ configureSequence(seq, DEFAULT);
+ boolean succeeded = false;
+ try {
+ seq.open(txn, key, openFlags);
+ succeeded = true;
+ return seq;
+ } finally {
+ if (!succeeded)
+ try {
+ seq.close(0);
+ } catch (Throwable t) {
+ // Ignore it -- an exception is already in flight.
+ }
+ }
+ }
+
+ /* package */
+ void configureSequence(final DbSequence seq, final SequenceConfig oldConfig)
+ throws DatabaseException {
+
+ int seqFlags = 0;
+ seqFlags |= decrement ? DbConstants.DB_SEQ_DEC : DbConstants.DB_SEQ_INC;
+ seqFlags |= wrap ? DbConstants.DB_SEQ_WRAP : 0;
+
+ if (seqFlags != 0)
+ seq.set_flags(seqFlags);
+
+ if (rangeMin != oldConfig.rangeMin || rangeMax != oldConfig.rangeMax)
+ seq.set_range(rangeMin, rangeMax);
+
+ if (initialValue != oldConfig.initialValue)
+ seq.initial_value(initialValue);
+
+ if (cacheSize != oldConfig.cacheSize)
+ seq.set_cachesize(cacheSize);
+ }
+
+ /* package */
+ SequenceConfig(final DbSequence seq)
+ throws DatabaseException {
+
+ // XXX: can't get open flags
+ final int openFlags = 0;
+ allowCreate = (openFlags & DbConstants.DB_CREATE) != 0;
+ exclusiveCreate = (openFlags & DbConstants.DB_EXCL) != 0;
+
+ final int seqFlags = seq.get_flags();
+ decrement = (seqFlags & DbConstants.DB_SEQ_DEC) != 0;
+ wrap = (seqFlags & DbConstants.DB_SEQ_WRAP) != 0;
+
+ // XXX: can't get initial value
+ final long initialValue = 0;
+
+ cacheSize = seq.get_cachesize();
+
+ rangeMin = seq.get_range_min();
+ rangeMax = seq.get_range_max();
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/SequenceStats.java b/db/java/src/com/sleepycat/db/SequenceStats.java
new file mode 100644
index 000000000..5c1e3a868
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/SequenceStats.java
@@ -0,0 +1,74 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+public class SequenceStats {
+ // no public constructor
+ protected SequenceStats() {}
+
+ private int st_wait;
+ public int getWait() {
+ return st_wait;
+ }
+
+ private int st_nowait;
+ public int getNowait() {
+ return st_nowait;
+ }
+
+ private long st_current;
+ public long getCurrent() {
+ return st_current;
+ }
+
+ private long st_value;
+ public long getValue() {
+ return st_value;
+ }
+
+ private long st_last_value;
+ public long getLastValue() {
+ return st_last_value;
+ }
+
+ private long st_min;
+ public long getMin() {
+ return st_min;
+ }
+
+ private long st_max;
+ public long getMax() {
+ return st_max;
+ }
+
+ private int st_cache_size;
+ public int getCacheSize() {
+ return st_cache_size;
+ }
+
+ private int st_flags;
+ public int getFlags() {
+ return st_flags;
+ }
+
+ public String toString() {
+ return "SequenceStats:"
+ + "\n st_wait=" + st_wait
+ + "\n st_nowait=" + st_nowait
+ + "\n st_current=" + st_current
+ + "\n st_value=" + st_value
+ + "\n st_last_value=" + st_last_value
+ + "\n st_min=" + st_min
+ + "\n st_max=" + st_max
+ + "\n st_cache_size=" + st_cache_size
+ + "\n st_flags=" + st_flags
+ ;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/StatsConfig.java b/db/java/src/com/sleepycat/db/StatsConfig.java
new file mode 100644
index 000000000..400407960
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/StatsConfig.java
@@ -0,0 +1,56 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: StatsConfig.java,v 1.3 2004/04/21 01:09:10 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public class StatsConfig {
+ /*
+ * For internal use, to allow null as a valid value for
+ * the config parameter.
+ */
+ public static final StatsConfig DEFAULT = new StatsConfig();
+
+ /* package */
+ static StatsConfig checkNull(StatsConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ private boolean clear = false;
+ private boolean fast = false;
+
+ public StatsConfig() {
+ }
+
+ public void setClear(boolean clear) {
+ this.clear = clear;
+ }
+
+ public boolean getClear() {
+ return clear;
+ }
+
+ public void setFast(boolean fast) {
+ this.fast = fast;
+ }
+
+ public boolean getFast() {
+ return fast;
+ }
+
+ int getFlags() {
+ int flags = 0;
+ if (fast)
+ flags |= DbConstants.DB_FAST_STAT;
+ if (clear)
+ flags |= DbConstants.DB_STAT_CLEAR;
+ return flags;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/Transaction.java b/db/java/src/com/sleepycat/db/Transaction.java
new file mode 100644
index 000000000..7aacc86ed
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/Transaction.java
@@ -0,0 +1,75 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: Transaction.java,v 1.2 2004/04/21 01:09:10 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbTxn;
+
+public class Transaction {
+ /*package */ final DbTxn txn;
+
+ Transaction(final DbTxn txn) {
+ this.txn = txn;
+ }
+
+ public void abort()
+ throws DatabaseException {
+
+ txn.abort();
+ }
+
+ public void commit()
+ throws DatabaseException {
+
+ txn.commit(0);
+ }
+
+ public void commitSync()
+ throws DatabaseException {
+
+ txn.commit(DbConstants.DB_TXN_SYNC);
+ }
+
+ public void commitNoSync()
+ throws DatabaseException {
+
+ txn.commit(DbConstants.DB_TXN_NOSYNC);
+ }
+
+ public void discard()
+ throws DatabaseException {
+
+ txn.discard(0);
+ }
+
+ public int getId()
+ throws DatabaseException {
+
+ return txn.id();
+ }
+
+ public void prepare(final byte[] gid)
+ throws DatabaseException {
+
+ txn.prepare(gid);
+ }
+
+ public void setTxnTimeout(final long timeOut)
+ throws DatabaseException {
+
+ txn.set_timeout(timeOut, DbConstants.DB_SET_TXN_TIMEOUT);
+ }
+
+ public void setLockTimeout(final long timeOut)
+ throws DatabaseException {
+
+ txn.set_timeout(timeOut, DbConstants.DB_SET_LOCK_TIMEOUT);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/TransactionConfig.java b/db/java/src/com/sleepycat/db/TransactionConfig.java
new file mode 100644
index 000000000..6d00225cc
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/TransactionConfig.java
@@ -0,0 +1,89 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: TransactionConfig.java,v 1.3 2004/09/28 19:30:37 mjc Exp $
+ */
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+import com.sleepycat.db.internal.DbEnv;
+import com.sleepycat.db.internal.DbTxn;
+
+public class TransactionConfig implements Cloneable {
+ /*
+ * For internal use, to allow null as a valid value for
+ * the config parameter.
+ */
+ public static final TransactionConfig DEFAULT = new TransactionConfig();
+
+ /* package */
+ static TransactionConfig checkNull(TransactionConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ private boolean dirtyRead = false;
+ private boolean degree2 = false;
+ private boolean noSync = false;
+ private boolean noWait = false;
+ private boolean sync = false;
+
+ public TransactionConfig() {
+ }
+
+ public void setDegree2(final boolean degree2) {
+ this.degree2 = degree2;
+ }
+
+ public boolean getDegree2() {
+ return degree2;
+ }
+
+ public void setDirtyRead(final boolean dirtyRead) {
+ this.dirtyRead = dirtyRead;
+ }
+
+ public boolean getDirtyRead() {
+ return dirtyRead;
+ }
+
+ public void setNoSync(final boolean noSync) {
+ this.noSync = noSync;
+ }
+
+ public boolean getNoSync() {
+ return noSync;
+ }
+
+ public void setNoWait(final boolean noWait) {
+ this.noWait = noWait;
+ }
+
+ public boolean getNoWait() {
+ return noWait;
+ }
+
+ public void setSync(final boolean sync) {
+ this.sync = sync;
+ }
+
+ public boolean getSync() {
+ return sync;
+ }
+
+ DbTxn beginTransaction(final DbEnv dbenv, final DbTxn parent)
+ throws DatabaseException {
+
+ int flags = 0;
+ flags |= degree2 ? DbConstants.DB_DEGREE_2 : 0;
+ flags |= dirtyRead ? DbConstants.DB_DIRTY_READ : 0;
+ flags |= noSync ? DbConstants.DB_TXN_NOSYNC : 0;
+ flags |= noWait ? DbConstants.DB_TXN_NOWAIT : 0;
+ flags |= sync ? DbConstants.DB_TXN_SYNC : 0;
+
+ return dbenv.txn_begin(parent, flags);
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/TransactionStats.java b/db/java/src/com/sleepycat/db/TransactionStats.java
new file mode 100644
index 000000000..a872cde4c
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/TransactionStats.java
@@ -0,0 +1,147 @@
+/*-
+ * DO NOT EDIT: automatically built by dist/s_java_stat.
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2004
+ * Sleepycat Software. All rights reserved.
+ */
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbUtil;
+
+public class TransactionStats
+{
+ // no public constructor
+ protected TransactionStats() {}
+
+ public static class Active { // no public constructor
+ protected Active() {}
+
+ private int txnid;
+ public int getTxnId() {
+ return txnid;
+ }
+
+ private int parentid;
+ public int getParentId() {
+ return parentid;
+ }
+
+ private LogSequenceNumber lsn;
+ public LogSequenceNumber getLsn() {
+ return lsn;
+ }
+
+ private int xa_status;
+ public int getXaStatus() {
+ return xa_status;
+ }
+
+ private byte[] xid;
+ public byte[] getXId() {
+ return xid;
+ }
+
+ public String toString() {
+ return "Active:"
+ + "\n txnid=" + txnid
+ + "\n parentid=" + parentid
+ + "\n lsn=" + lsn
+ + "\n xa_status=" + xa_status
+ + "\n xid=" + DbUtil.byteArrayToString(xid)
+ ;
+ }
+ };
+
+ private LogSequenceNumber st_last_ckp;
+ public LogSequenceNumber getLastCkp() {
+ return st_last_ckp;
+ }
+
+ private long st_time_ckp;
+ public long getTimeCkp() {
+ return st_time_ckp;
+ }
+
+ private int st_last_txnid;
+ public int getLastTxnId() {
+ return st_last_txnid;
+ }
+
+ private int st_maxtxns;
+ public int getMaxTxns() {
+ return st_maxtxns;
+ }
+
+ private int st_naborts;
+ public int getNaborts() {
+ return st_naborts;
+ }
+
+ private int st_nbegins;
+ public int getNumBegins() {
+ return st_nbegins;
+ }
+
+ private int st_ncommits;
+ public int getNumCommits() {
+ return st_ncommits;
+ }
+
+ private int st_nactive;
+ public int getNactive() {
+ return st_nactive;
+ }
+
+ private int st_nrestores;
+ public int getNumRestores() {
+ return st_nrestores;
+ }
+
+ private int st_maxnactive;
+ public int getMaxNactive() {
+ return st_maxnactive;
+ }
+
+ private Active[] st_txnarray;
+ public Active[] getTxnarray() {
+ return st_txnarray;
+ }
+
+ private int st_region_wait;
+ public int getRegionWait() {
+ return st_region_wait;
+ }
+
+ private int st_region_nowait;
+ public int getRegionNowait() {
+ return st_region_nowait;
+ }
+
+ private int st_regsize;
+ public int getRegSize() {
+ return st_regsize;
+ }
+
+ public String toString() {
+ return "TransactionStats:"
+ + "\n st_last_ckp=" + st_last_ckp
+ + "\n st_time_ckp=" + st_time_ckp
+ + "\n st_last_txnid=" + st_last_txnid
+ + "\n st_maxtxns=" + st_maxtxns
+ + "\n st_naborts=" + st_naborts
+ + "\n st_nbegins=" + st_nbegins
+ + "\n st_ncommits=" + st_ncommits
+ + "\n st_nactive=" + st_nactive
+ + "\n st_nrestores=" + st_nrestores
+ + "\n st_maxnactive=" + st_maxnactive
+ + "\n st_txnarray=" + DbUtil.objectArrayToString(st_txnarray, "st_txnarray")
+ + "\n st_region_wait=" + st_region_wait
+ + "\n st_region_nowait=" + st_region_nowait
+ + "\n st_regsize=" + st_regsize
+ ;
+ }
+}
+// end of TransactionStats.java
diff --git a/db/java/src/com/sleepycat/db/VerifyConfig.java b/db/java/src/com/sleepycat/db/VerifyConfig.java
new file mode 100644
index 000000000..0bb4031e0
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/VerifyConfig.java
@@ -0,0 +1,81 @@
+/*-
+* See the file LICENSE for redistribution information.
+*
+* Copyright (c) 2002-2004
+* Sleepycat Software. All rights reserved.
+*
+* $Id: VerifyConfig.java,v 1.3 2004/04/21 01:09:10 mjc Exp $
+*/
+
+package com.sleepycat.db;
+
+import com.sleepycat.db.internal.DbConstants;
+
+public class VerifyConfig {
+ public static final VerifyConfig DEFAULT = new VerifyConfig();
+
+ /* package */
+ static VerifyConfig checkNull(VerifyConfig config) {
+ return (config == null) ? DEFAULT : config;
+ }
+
+ private boolean aggressive = false;
+ private boolean noOrderCheck = false;
+ private boolean orderCheckOnly = false;
+ private boolean salvage = false;
+ private boolean printable = false;
+
+ public VerifyConfig() {
+ }
+
+ public void setAggressive(final boolean aggressive) {
+ this.aggressive = aggressive;
+ }
+
+ public boolean getAggressive() {
+ return aggressive;
+ }
+
+ public void setNoOrderCheck(final boolean noOrderCheck) {
+ this.noOrderCheck = noOrderCheck;
+ }
+
+ public boolean getNoOrderCheck() {
+ return printable;
+ }
+
+ public void setOrderCheckOnly(final boolean orderCheckOnly) {
+ this.orderCheckOnly = orderCheckOnly;
+ }
+
+ public boolean getOrderCheckOnly() {
+ return orderCheckOnly;
+ }
+
+ public void setPrintable(final boolean printable) {
+ this.printable = printable;
+ }
+
+ public boolean getPrintable() {
+ return printable;
+ }
+
+ public void setSalvage(final boolean salvage) {
+ this.salvage = salvage;
+ }
+
+ public boolean getSalvage() {
+ return salvage;
+ }
+
+ int getFlags() {
+ int flags = 0;
+ flags |= aggressive ? DbConstants.DB_AGGRESSIVE : 0;
+ flags |= noOrderCheck ? DbConstants.DB_NOORDERCHK : 0;
+ flags |= orderCheckOnly ? DbConstants.DB_ORDERCHKONLY : 0;
+ flags |= salvage ? DbConstants.DB_SALVAGE : 0;
+ flags |= printable ? DbConstants.DB_PRINTABLE : 0;
+
+ return flags;
+ }
+}
diff --git a/db/java/src/com/sleepycat/db/internal/Db.java b/db/java/src/com/sleepycat/db/internal/Db.java
new file mode 100644
index 000000000..0bee8f4c1
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/Db.java
@@ -0,0 +1,399 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package com.sleepycat.db.internal;
+
+
+import com.sleepycat.db.*;
+import java.util.Comparator;
+
+public class Db {
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ protected Db(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ protected Db() {
+ this(0, false);
+ }
+
+ /* package */ void delete() {
+ if(swigCPtr != 0 && swigCMemOwn) {
+ swigCMemOwn = false;
+ throw new UnsupportedOperationException("C++ destructor does not have public access");
+ }
+ swigCPtr = 0;
+ }
+
+ protected static long getCPtr(Db obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ /* package */ static final int GIGABYTE = 1 << 30;
+ /*
+ * Internally, the JNI layer creates a global reference to each Db,
+ * which can potentially be different to this. We keep a copy here so
+ * we can clean up after destructors.
+ */
+ private long db_ref;
+ private DbEnv dbenv;
+ private boolean private_dbenv;
+
+ public Database wrapper;
+ private RecordNumberAppender append_recno_handler;
+ private Comparator bt_compare_handler;
+ private BtreePrefixCalculator bt_prefix_handler;
+ private Comparator dup_compare_handler;
+ private FeedbackHandler db_feedback_handler;
+ private Hasher h_hash_handler;
+ private SecondaryKeyCreator seckey_create_handler;
+
+ /* Called by the Db constructor */
+ private void initialize(DbEnv dbenv) {
+ if (dbenv == null) {
+ private_dbenv = true;
+ dbenv = db_java.getDbEnv0(this);
+ dbenv.initialize();
+ }
+ this.dbenv = dbenv;
+ db_ref = db_java.initDbRef0(this, this);
+ }
+
+ private void cleanup() {
+ swigCPtr = 0;
+ db_java.deleteRef0(db_ref);
+ db_ref = 0L;
+ if (private_dbenv)
+ dbenv.cleanup();
+ dbenv = null;
+ }
+
+ public synchronized void close(int flags) throws DatabaseException {
+ try {
+ close0(flags);
+ } finally {
+ cleanup();
+ }
+ }
+
+ public DbEnv get_env() throws DatabaseException {
+ return dbenv;
+ }
+
+ private final void handle_append_recno(DatabaseEntry data, int recno)
+ throws DatabaseException {
+ append_recno_handler.appendRecordNumber(wrapper, data, recno);
+ }
+
+ public RecordNumberAppender get_append_recno() throws com.sleepycat.db.DatabaseException {
+ return append_recno_handler;
+ }
+
+ private final int handle_bt_compare(DatabaseEntry dbt1, DatabaseEntry dbt2) {
+ return bt_compare_handler.compare(dbt1, dbt2);
+ }
+
+ public Comparator get_bt_compare() throws com.sleepycat.db.DatabaseException {
+ return bt_compare_handler;
+ }
+
+ private final int handle_bt_prefix(DatabaseEntry dbt1, DatabaseEntry dbt2) {
+ return bt_prefix_handler.prefix(wrapper, dbt1, dbt2);
+ }
+
+ public BtreePrefixCalculator get_bt_prefix() throws com.sleepycat.db.DatabaseException {
+ return bt_prefix_handler;
+ }
+
+ private final void handle_db_feedback(int opcode, int percent) {
+ if (opcode == DbConstants.DB_UPGRADE)
+ db_feedback_handler.upgradeFeedback(wrapper, percent);
+ else if (opcode == DbConstants.DB_VERIFY)
+ db_feedback_handler.upgradeFeedback(wrapper, percent);
+ /* No other database feedback types known. */
+ }
+
+ public FeedbackHandler get_feedback() throws com.sleepycat.db.DatabaseException {
+ return db_feedback_handler;
+ }
+
+ private final int handle_dup_compare(DatabaseEntry dbt1, DatabaseEntry dbt2) {
+ return dup_compare_handler.compare(dbt1, dbt2);
+ }
+
+ public Comparator get_dup_compare() throws com.sleepycat.db.DatabaseException {
+ return dup_compare_handler;
+ }
+
+ private final int handle_h_hash(byte[] data, int len) {
+ return h_hash_handler.hash(wrapper, data, len);
+ }
+
+ public Hasher get_h_hash() throws com.sleepycat.db.DatabaseException {
+ return h_hash_handler;
+ }
+
+ private final int handle_seckey_create(DatabaseEntry key, DatabaseEntry data, DatabaseEntry result)
+ throws DatabaseException {
+ return seckey_create_handler.createSecondaryKey(
+ (SecondaryDatabase)wrapper, key, data, result) ?
+ 0 : DbConstants.DB_DONOTINDEX;
+ }
+
+ public SecondaryKeyCreator get_seckey_create() throws com.sleepycat.db.DatabaseException {
+ return seckey_create_handler;
+ }
+
+ public synchronized void remove(String file, String database, int flags)
+ throws DatabaseException, java.io.FileNotFoundException {
+ try {
+ remove0(file, database, flags);
+ } finally {
+ cleanup();
+ }
+ }
+
+ public synchronized void rename(String file, String database,
+ String newname, int flags)
+ throws DatabaseException, java.io.FileNotFoundException {
+ try {
+ rename0(file, database, newname, flags);
+ } finally {
+ cleanup();
+ }
+ }
+
+ public synchronized boolean verify(String file, String database,
+ java.io.OutputStream outfile, int flags)
+ throws DatabaseException, java.io.FileNotFoundException {
+ try {
+ return verify0(file, database, outfile, flags);
+ } finally {
+ cleanup();
+ }
+ }
+
+ public ErrorHandler get_errcall() /* no exception */ {
+ return dbenv.get_errcall();
+ }
+
+ public void set_errcall(ErrorHandler db_errcall_fcn) /* no exception */ {
+ dbenv.set_errcall(db_errcall_fcn);
+ }
+
+ public MessageHandler get_msgcall() /* no exception */ {
+ return dbenv.get_msgcall();
+ }
+
+ public void set_msgcall(MessageHandler db_msgcall_fcn) /* no exception */ {
+ dbenv.set_msgcall(db_msgcall_fcn);
+ }
+
+ public java.io.OutputStream get_error_stream() /* no exception */ {
+ return dbenv.get_error_stream();
+ }
+
+ public void set_error_stream(java.io.OutputStream stream) /* no exception */ {
+ dbenv.set_error_stream(stream);
+ }
+
+ public java.io.OutputStream get_message_stream() /* no exception */ {
+ return dbenv.get_message_stream();
+ }
+
+ public void set_message_stream(java.io.OutputStream stream) /* no exception */ {
+ dbenv.set_message_stream(stream);
+ }
+
+ public void set_paniccall(PanicHandler db_panic_fcn)
+ throws DatabaseException {
+ dbenv.set_paniccall(db_panic_fcn);
+ }
+
+ public PanicHandler get_paniccall() throws com.sleepycat.db.DatabaseException {
+ return dbenv.get_paniccall();
+ }
+
+ public Db(DbEnv dbenv, int flags) throws com.sleepycat.db.DatabaseException {
+ this(db_javaJNI.new_Db(DbEnv.getCPtr(dbenv), flags), true);
+ initialize(dbenv);
+ }
+
+ public void associate(DbTxn txnid, Db secondary, com.sleepycat.db.SecondaryKeyCreator callback, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_associate(swigCPtr, DbTxn.getCPtr(txnid), Db.getCPtr(secondary), (secondary.seckey_create_handler = callback) , flags); }
+
+ /* package */ int close0(int flags) {
+ return db_javaJNI.Db_close0(swigCPtr, flags);
+ }
+
+ public Dbc cursor(DbTxn txnid, int flags) throws com.sleepycat.db.DatabaseException {
+ long cPtr = db_javaJNI.Db_cursor(swigCPtr, DbTxn.getCPtr(txnid), flags);
+ return (cPtr == 0) ? null : new Dbc(cPtr, false);
+ }
+
+ public int del(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_del(swigCPtr, DbTxn.getCPtr(txnid), key, flags);
+ }
+
+ public void err(int error, String message) /* no exception */ {
+ db_javaJNI.Db_err(swigCPtr, error, message);
+ }
+
+ public void errx(String message) /* no exception */ {
+ db_javaJNI.Db_errx(swigCPtr, message);
+ }
+
+ public boolean get_transactional() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_transactional(swigCPtr); }
+
+ public int get(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get(swigCPtr, DbTxn.getCPtr(txnid), key, data, flags);
+ }
+
+ public boolean get_byteswapped() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_byteswapped(swigCPtr); }
+
+ public long get_cachesize() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get_cachesize(swigCPtr);
+ }
+
+ public int get_cachesize_ncache() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_cachesize_ncache(swigCPtr); }
+
+ public String get_filename() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get_filename(swigCPtr);
+ }
+
+ public String get_dbname() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get_dbname(swigCPtr);
+ }
+
+ public int get_encrypt_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_encrypt_flags(swigCPtr); }
+
+ public String get_errpfx() /* no exception */ {
+ return db_javaJNI.Db_get_errpfx(swigCPtr);
+ }
+
+ public int get_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_flags(swigCPtr); }
+
+ public int get_lorder() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get_lorder(swigCPtr);
+ }
+
+ public DbMpoolFile get_mpf() throws com.sleepycat.db.DatabaseException {
+ long cPtr = db_javaJNI.Db_get_mpf(swigCPtr);
+ return (cPtr == 0) ? null : new DbMpoolFile(cPtr, false);
+ }
+
+ public int get_open_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_open_flags(swigCPtr); }
+
+ public int get_pagesize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_pagesize(swigCPtr); }
+
+ public int get_bt_minkey() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_bt_minkey(swigCPtr); }
+
+ public int get_h_ffactor() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_h_ffactor(swigCPtr); }
+
+ public int get_h_nelem() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_h_nelem(swigCPtr); }
+
+ public int get_re_delim() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get_re_delim(swigCPtr);
+ }
+
+ public int get_re_len() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_re_len(swigCPtr); }
+
+ public int get_re_pad() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get_re_pad(swigCPtr);
+ }
+
+ public String get_re_source() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get_re_source(swigCPtr);
+ }
+
+ public int get_q_extentsize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_q_extentsize(swigCPtr); }
+
+ public int get_type() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_get_type(swigCPtr);
+ }
+
+ public Dbc join(Dbc[] curslist, int flags) throws com.sleepycat.db.DatabaseException {
+ long cPtr = db_javaJNI.Db_join(swigCPtr, curslist, flags);
+ return (cPtr == 0) ? null : new Dbc(cPtr, true);
+ }
+
+ public void key_range(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.KeyRange key_range, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_key_range(swigCPtr, DbTxn.getCPtr(txnid), key, key_range, flags); }
+
+ public void open(DbTxn txnid, String file, String database, int type, int flags, int mode) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException { db_javaJNI.Db_open(swigCPtr, DbTxn.getCPtr(txnid), file, database, type, flags, mode); }
+
+ public int pget(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry pkey, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_pget(swigCPtr, DbTxn.getCPtr(txnid), key, pkey, data, flags);
+ }
+
+ public int put(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_put(swigCPtr, DbTxn.getCPtr(txnid), key, data, flags);
+ }
+
+ /* package */ void remove0(String file, String database, int flags) { db_javaJNI.Db_remove0(swigCPtr, file, database, flags); }
+
+ /* package */ void rename0(String file, String database, String newname, int flags) { db_javaJNI.Db_rename0(swigCPtr, file, database, newname, flags); }
+
+ public void set_append_recno(com.sleepycat.db.RecordNumberAppender db_append_recno_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_append_recno(swigCPtr, (append_recno_handler = db_append_recno_fcn) ); }
+
+ public void set_bt_compare(java.util.Comparator bt_compare_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_bt_compare(swigCPtr, (bt_compare_handler = bt_compare_fcn) ); }
+
+ public void set_bt_maxkey(int maxkey) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_bt_maxkey(swigCPtr, maxkey); }
+
+ public void set_bt_minkey(int bt_minkey) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_bt_minkey(swigCPtr, bt_minkey); }
+
+ public void set_bt_prefix(com.sleepycat.db.BtreePrefixCalculator bt_prefix_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_bt_prefix(swigCPtr, (bt_prefix_handler = bt_prefix_fcn) ); }
+
+ public void set_cachesize(long bytes, int ncache) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_cachesize(swigCPtr, bytes, ncache); }
+
+ public void set_dup_compare(java.util.Comparator dup_compare_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_dup_compare(swigCPtr, (dup_compare_handler = dup_compare_fcn) ); }
+
+ public void set_encrypt(String passwd, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_encrypt(swigCPtr, passwd, flags); }
+
+ public void set_errpfx(String errpfx) /* no exception */ {
+ db_javaJNI.Db_set_errpfx(swigCPtr, errpfx);
+ }
+
+ public void set_feedback(com.sleepycat.db.FeedbackHandler db_feedback_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_feedback(swigCPtr, (db_feedback_handler = db_feedback_fcn) ); }
+
+ public void set_flags(int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_flags(swigCPtr, flags); }
+
+ public void set_h_ffactor(int h_ffactor) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_h_ffactor(swigCPtr, h_ffactor); }
+
+ public void set_h_hash(com.sleepycat.db.Hasher h_hash_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_h_hash(swigCPtr, (h_hash_handler = h_hash_fcn) ); }
+
+ public void set_h_nelem(int h_nelem) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_h_nelem(swigCPtr, h_nelem); }
+
+ public void set_lorder(int lorder) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_lorder(swigCPtr, lorder); }
+
+ public void set_pagesize(long pagesize) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_pagesize(swigCPtr, pagesize); }
+
+ public void set_re_delim(int re_delim) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_re_delim(swigCPtr, re_delim); }
+
+ public void set_re_len(int re_len) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_re_len(swigCPtr, re_len); }
+
+ public void set_re_pad(int re_pad) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_re_pad(swigCPtr, re_pad); }
+
+ public void set_re_source(String source) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_re_source(swigCPtr, source); }
+
+ public void set_q_extentsize(int extentsize) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_q_extentsize(swigCPtr, extentsize); }
+
+ public Object stat(DbTxn txnid, int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_stat(swigCPtr, DbTxn.getCPtr(txnid), flags); }
+
+ public void sync(int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_sync(swigCPtr, flags); }
+
+ public int truncate(DbTxn txnid, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Db_truncate(swigCPtr, DbTxn.getCPtr(txnid), flags);
+ }
+
+ public void upgrade(String file, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_upgrade(swigCPtr, file, flags); }
+
+ /* package */ boolean verify0(String file, String database, java.io.OutputStream outfile, int flags) { return db_javaJNI.Db_verify0(swigCPtr, file, database, outfile, flags); }
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/DbClient.java b/db/java/src/com/sleepycat/db/internal/DbClient.java
new file mode 100644
index 000000000..28d586ecb
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbClient.java
@@ -0,0 +1,17 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2003
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DbClient.java,v 1.1 2004/04/06 20:43:40 mjc Exp $
+ */
+package com.sleepycat.db.internal;
+
+/**
+ * The DbClient object is used to encapsulate a reference to an RPC
+ * client.</p>
+ */
+public interface DbClient {
+}
diff --git a/db/java/src/com/sleepycat/db/internal/DbConstants.java b/db/java/src/com/sleepycat/db/internal/DbConstants.java
new file mode 100644
index 000000000..0ad378cd6
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbConstants.java
@@ -0,0 +1,182 @@
+/* DO NOT EDIT: automatically built by dist/s_java_const. */
+
+package com.sleepycat.db.internal;
+
+public interface DbConstants
+{
+ int DB_AFTER = 1;
+ int DB_AGGRESSIVE = 0x0000001;
+ int DB_APPEND = 2;
+ int DB_ARCH_ABS = 0x001;
+ int DB_ARCH_DATA = 0x002;
+ int DB_ARCH_LOG = 0x004;
+ int DB_ARCH_REMOVE = 0x008;
+ int DB_AUTO_COMMIT = 0x01000000;
+ int DB_BEFORE = 3;
+ int DB_BTREE = 1;
+ int DB_CDB_ALLDB = 0x00001000;
+ int DB_CHKSUM = 0x0000001;
+ int DB_CONSUME = 5;
+ int DB_CONSUME_WAIT = 6;
+ int DB_CREATE = 0x0000001;
+ int DB_CURRENT = 7;
+ int DB_DBT_MALLOC = 0x004;
+ int DB_DBT_PARTIAL = 0x008;
+ int DB_DBT_USERMEM = 0x020;
+ int DB_DEGREE_2 = 0x02000000;
+ int DB_DIRECT_DB = 0x00002000;
+ int DB_DIRECT_LOG = 0x00004000;
+ int DB_DIRTY_READ = 0x04000000;
+ int DB_DONOTINDEX = -30998;
+ int DB_DSYNC_LOG = 0x00008000;
+ int DB_DUP = 0x0000002;
+ int DB_DUPSORT = 0x0000004;
+ int DB_EID_BROADCAST = -1;
+ int DB_EID_INVALID = -2;
+ int DB_ENCRYPT = 0x0000008;
+ int DB_ENCRYPT_AES = 0x0000001;
+ int DB_EXCL = 0x0001000;
+ int DB_FAST_STAT = 8;
+ int DB_FIRST = 9;
+ int DB_FLUSH = 0x001;
+ int DB_FORCE = 0x0000004;
+ int DB_GET_BOTH = 10;
+ int DB_GET_BOTH_RANGE = 12;
+ int DB_GET_RECNO = 13;
+ int DB_HASH = 2;
+ int DB_INIT_CDB = 0x0001000;
+ int DB_INIT_LOCK = 0x0002000;
+ int DB_INIT_LOG = 0x0004000;
+ int DB_INIT_MPOOL = 0x0008000;
+ int DB_INIT_REP = 0x0010000;
+ int DB_INIT_TXN = 0x0020000;
+ int DB_INORDER = 0x0000010;
+ int DB_JOINENV = 0x0040000;
+ int DB_JOIN_ITEM = 14;
+ int DB_JOIN_NOSORT = 0x0000001;
+ int DB_KEYEMPTY = -30997;
+ int DB_KEYEXIST = -30996;
+ int DB_KEYFIRST = 15;
+ int DB_KEYLAST = 16;
+ int DB_LAST = 17;
+ int DB_LOCKDOWN = 0x0080000;
+ int DB_LOCK_DEFAULT = 1;
+ int DB_LOCK_EXPIRE = 2;
+ int DB_LOCK_GET = 1;
+ int DB_LOCK_GET_TIMEOUT = 2;
+ int DB_LOCK_IREAD = 5;
+ int DB_LOCK_IWR = 6;
+ int DB_LOCK_IWRITE = 4;
+ int DB_LOCK_MAXLOCKS = 3;
+ int DB_LOCK_MAXWRITE = 4;
+ int DB_LOCK_MINLOCKS = 5;
+ int DB_LOCK_MINWRITE = 6;
+ int DB_LOCK_NOTGRANTED = -30994;
+ int DB_LOCK_NOWAIT = 0x002;
+ int DB_LOCK_OLDEST = 7;
+ int DB_LOCK_PUT = 4;
+ int DB_LOCK_PUT_ALL = 5;
+ int DB_LOCK_PUT_OBJ = 6;
+ int DB_LOCK_RANDOM = 8;
+ int DB_LOCK_READ = 1;
+ int DB_LOCK_TIMEOUT = 8;
+ int DB_LOCK_WRITE = 2;
+ int DB_LOCK_YOUNGEST = 9;
+ int DB_LOG_AUTOREMOVE = 0x00010000;
+ int DB_LOG_INMEMORY = 0x00020000;
+ int DB_MPOOL_NOFILE = 0x001;
+ int DB_MPOOL_UNLINK = 0x002;
+ int DB_MULTIPLE = 0x08000000;
+ int DB_MULTIPLE_KEY = 0x10000000;
+ int DB_NEXT = 18;
+ int DB_NEXT_DUP = 19;
+ int DB_NEXT_NODUP = 20;
+ int DB_NODUPDATA = 21;
+ int DB_NOLOCKING = 0x00040000;
+ int DB_NOMMAP = 0x0000008;
+ int DB_NOORDERCHK = 0x0000002;
+ int DB_NOOVERWRITE = 22;
+ int DB_NOPANIC = 0x00080000;
+ int DB_NOSERVER_HOME = -30991;
+ int DB_NOSERVER_ID = -30990;
+ int DB_NOSYNC = 23;
+ int DB_NOTFOUND = -30989;
+ int DB_ORDERCHKONLY = 0x0000004;
+ int DB_OVERWRITE = 0x00100000;
+ int DB_PANIC_ENVIRONMENT = 0x00200000;
+ int DB_POSITION = 24;
+ int DB_PREV = 25;
+ int DB_PREV_NODUP = 26;
+ int DB_PRINTABLE = 0x0000020;
+ int DB_PRIORITY_DEFAULT = 3;
+ int DB_PRIORITY_HIGH = 4;
+ int DB_PRIORITY_LOW = 2;
+ int DB_PRIORITY_VERY_HIGH = 5;
+ int DB_PRIORITY_VERY_LOW = 1;
+ int DB_PRIVATE = 0x0100000;
+ int DB_QUEUE = 4;
+ int DB_RDONLY = 0x0000010;
+ int DB_RECNO = 3;
+ int DB_RECNUM = 0x0000020;
+ int DB_RECOVER = 0x0000020;
+ int DB_RECOVER_FATAL = 0x0200000;
+ int DB_REGION_INIT = 0x00400000;
+ int DB_RENUMBER = 0x0000040;
+ int DB_REP_CLIENT = 0x001;
+ int DB_REP_DUPMASTER = -30986;
+ int DB_REP_HOLDELECTION = -30984;
+ int DB_REP_ISPERM = -30983;
+ int DB_REP_MASTER = 0x002;
+ int DB_REP_NEWMASTER = -30982;
+ int DB_REP_NEWSITE = -30981;
+ int DB_REP_NOBUFFER = 0x0000001;
+ int DB_REP_NOTPERM = -30980;
+ int DB_REP_PERMANENT = 0x0000002;
+ int DB_REP_STARTUPDONE = -30979;
+ int DB_REVSPLITOFF = 0x0000080;
+ int DB_RMW = 0x20000000;
+ int DB_RPCCLIENT = 0x0000001;
+ int DB_SALVAGE = 0x0000040;
+ int DB_SEQ_DEC = 0x00000001;
+ int DB_SEQ_INC = 0x00000002;
+ int DB_SEQ_WRAP = 0x00000008;
+ int DB_SET = 28;
+ int DB_SET_LOCK_TIMEOUT = 29;
+ int DB_SET_RANGE = 30;
+ int DB_SET_RECNO = 31;
+ int DB_SET_TXN_TIMEOUT = 33;
+ int DB_SNAPSHOT = 0x0000100;
+ int DB_STAT_CLEAR = 0x0000002;
+ int DB_SYSTEM_MEM = 0x0400000;
+ int DB_THREAD = 0x0000040;
+ int DB_TIME_NOTGRANTED = 0x00800000;
+ int DB_TRUNCATE = 0x0000080;
+ int DB_TXN_ABORT = 0;
+ int DB_TXN_APPLY = 1;
+ int DB_TXN_BACKWARD_ROLL = 3;
+ int DB_TXN_FORWARD_ROLL = 4;
+ int DB_TXN_NOSYNC = 0x0000100;
+ int DB_TXN_NOT_DURABLE = 0x0000200;
+ int DB_TXN_NOWAIT = 0x0001000;
+ int DB_TXN_PRINT = 7;
+ int DB_TXN_SYNC = 0x0002000;
+ int DB_TXN_WRITE_NOSYNC = 0x10000000;
+ int DB_UNKNOWN = 5;
+ int DB_UPGRADE = 0x0000001;
+ int DB_USE_ENVIRON = 0x0000400;
+ int DB_USE_ENVIRON_ROOT = 0x0000800;
+ int DB_VERB_DEADLOCK = 0x0001;
+ int DB_VERB_RECOVERY = 0x0002;
+ int DB_VERB_REPLICATION = 0x0004;
+ int DB_VERB_WAITSFOR = 0x0008;
+ int DB_VERIFY = 0x0000002;
+ int DB_VERSION_MAJOR = 4;
+ int DB_VERSION_MINOR = 3;
+ int DB_VERSION_PATCH = 14;
+ int DB_WRITECURSOR = 35;
+ int DB_XA_CREATE = 0x0000002;
+ int DB_XIDDATASIZE = 128;
+ int DB_YIELDCPU = 0x20000000;
+}
+
+// end of DbConstants.java
diff --git a/db/java/src/com/sleepycat/db/internal/DbEnv.java b/db/java/src/com/sleepycat/db/internal/DbEnv.java
new file mode 100644
index 000000000..1a8112943
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbEnv.java
@@ -0,0 +1,434 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package com.sleepycat.db.internal;
+
+
+import com.sleepycat.db.*;
+import java.util.Comparator;
+
+public class DbEnv {
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ protected DbEnv(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ protected DbEnv() {
+ this(0, false);
+ }
+
+ /* package */ void delete() {
+ if(swigCPtr != 0 && swigCMemOwn) {
+ swigCMemOwn = false;
+ throw new UnsupportedOperationException("C++ destructor does not have public access");
+ }
+ swigCPtr = 0;
+ }
+
+ protected static long getCPtr(DbEnv obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ /*
+ * Internally, the JNI layer creates a global reference to each DbEnv,
+ * which can potentially be different to this. We keep a copy here so
+ * we can clean up after destructors.
+ */
+ private long dbenv_ref;
+ public Environment wrapper;
+
+ private LogRecordHandler app_dispatch_handler;
+ private FeedbackHandler env_feedback_handler;
+ private ErrorHandler error_handler;
+ private MessageHandler message_handler;
+ private PanicHandler panic_handler;
+ private ReplicationTransport rep_transport_handler;
+ private java.io.OutputStream error_stream;
+ private java.io.OutputStream message_stream;
+
+ public static class RepProcessMessage {
+ public int envid;
+ }
+
+ /*
+ * Called by the public DbEnv constructor and for private environments
+ * by the Db constructor.
+ */
+ void initialize() {
+ dbenv_ref = db_java.initDbEnvRef0(this, this);
+ /* Start with System.err as the default error stream. */
+ set_error_stream(System.err);
+ set_message_stream(System.out);
+ }
+
+ void cleanup() {
+ swigCPtr = 0;
+ db_java.deleteRef0(dbenv_ref);
+ dbenv_ref = 0L;
+ }
+
+ public synchronized void close(int flags) throws DatabaseException {
+ try {
+ close0(flags);
+ } finally {
+ cleanup();
+ }
+ }
+
+ private final int handle_app_dispatch(DatabaseEntry dbt, LogSequenceNumber lsn, int recops) {
+ return app_dispatch_handler.handleLogRecord(wrapper, dbt, lsn, RecoveryOperation.fromFlag(recops));
+ }
+
+ public LogRecordHandler get_app_dispatch() throws com.sleepycat.db.DatabaseException {
+ return app_dispatch_handler;
+ }
+
+ private final void handle_env_feedback(int opcode, int percent) {
+ if (opcode == DbConstants.DB_RECOVER)
+ env_feedback_handler.recoveryFeedback(wrapper, percent);
+ /* No other environment feedback type supported. */
+ }
+
+ public FeedbackHandler get_feedback() throws com.sleepycat.db.DatabaseException {
+ return env_feedback_handler;
+ }
+
+ private final void handle_error(String errpfx, String msg) {
+ error_handler.error(wrapper, errpfx, msg);
+ }
+
+ public ErrorHandler get_errcall() /* no exception */ {
+ return error_handler;
+ }
+
+ private final void handle_message(String msg) {
+ message_handler.message(wrapper, msg);
+ }
+
+ public MessageHandler get_msgcall() /* no exception */ {
+ return message_handler;
+ }
+
+ private final void handle_panic(DatabaseException e) {
+ panic_handler.panic(wrapper, e);
+ }
+
+ public PanicHandler get_paniccall() throws com.sleepycat.db.DatabaseException {
+ return panic_handler;
+ }
+
+ private final int handle_rep_transport(DatabaseEntry control, DatabaseEntry rec,
+ LogSequenceNumber lsn, int envid, int flags)
+ throws DatabaseException {
+ return rep_transport_handler.send(wrapper, control, rec, lsn, envid,
+ (flags & DbConstants.DB_REP_NOBUFFER) != 0,
+ (flags & DbConstants.DB_REP_PERMANENT) != 0);
+ }
+
+ public void lock_vec(/*u_int32_t*/ int locker, int flags,
+ LockRequest[] list, int offset, int count) throws DatabaseException {
+ db_javaJNI.DbEnv_lock_vec(swigCPtr, locker, flags, list,
+ offset, count);
+ }
+
+ public synchronized void remove(String db_home, int flags)
+ throws DatabaseException, java.io.FileNotFoundException {
+ try {
+ remove0(db_home, flags);
+ } finally {
+ cleanup();
+ }
+ }
+
+ public void set_error_stream(java.io.OutputStream stream) /* no exception */ {
+ error_stream = stream;
+ final java.io.PrintWriter pw = new java.io.PrintWriter(stream);
+ set_errcall(new ErrorHandler() {
+ public void error(Environment env,
+ String prefix, String buf) /* no exception */ {
+ if (prefix != null)
+ pw.print(prefix + ": ");
+ pw.println(buf);
+ pw.flush();
+ }
+ });
+ }
+
+ public java.io.OutputStream get_error_stream() /* no exception */ {
+ return error_stream;
+ }
+
+
+ public void set_message_stream(java.io.OutputStream stream) /* no exception */ {
+ message_stream = stream;
+ final java.io.PrintWriter pw = new java.io.PrintWriter(stream);
+ set_msgcall(new MessageHandler() {
+ public void message(Environment env, String msg) /* no exception */
+ /* no exception */ {
+ pw.println(msg);
+ pw.flush();
+ }
+ });
+ }
+
+ public java.io.OutputStream get_message_stream() /* no exception */ {
+ return message_stream;
+ }
+
+ public void set_tx_timestamp(java.util.Date timestamp) throws com.sleepycat.db.DatabaseException {
+ set_tx_timestamp0(timestamp.getTime()/1000);
+ }
+
+ public DbEnv(int flags) throws com.sleepycat.db.DatabaseException {
+ this(db_javaJNI.new_DbEnv(flags), true);
+ initialize();
+ }
+
+ /* package */ void close0(int flags) { db_javaJNI.DbEnv_close0(swigCPtr, flags); }
+
+ public void dbremove(DbTxn txnid, String file, String database, int flags) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException { db_javaJNI.DbEnv_dbremove(swigCPtr, DbTxn.getCPtr(txnid), file, database, flags); }
+
+ public void dbrename(DbTxn txnid, String file, String database, String newname, int flags) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException { db_javaJNI.DbEnv_dbrename(swigCPtr, DbTxn.getCPtr(txnid), file, database, newname, flags); }
+
+ public void err(int error, String message) /* no exception */ {
+ db_javaJNI.DbEnv_err(swigCPtr, error, message);
+ }
+
+ public void errx(String message) /* no exception */ {
+ db_javaJNI.DbEnv_errx(swigCPtr, message);
+ }
+
+ public String[] get_data_dirs() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_data_dirs(swigCPtr); }
+
+ public int get_encrypt_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_encrypt_flags(swigCPtr); }
+
+ public String get_errpfx() /* no exception */ {
+ return db_javaJNI.DbEnv_get_errpfx(swigCPtr);
+ }
+
+ public int get_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_flags(swigCPtr); }
+
+ public String get_home() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_get_home(swigCPtr);
+ }
+
+ public int get_open_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_open_flags(swigCPtr); }
+
+ public long get_shm_key() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_shm_key(swigCPtr); }
+
+ public int get_tas_spins() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_tas_spins(swigCPtr); }
+
+ public String get_tmp_dir() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_get_tmp_dir(swigCPtr);
+ }
+
+ public boolean get_verbose(int which) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_verbose(swigCPtr, which); }
+
+ public void open(String db_home, int flags, int mode) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException { db_javaJNI.DbEnv_open(swigCPtr, db_home, flags, mode); }
+
+ /* package */ void remove0(String db_home, int flags) { db_javaJNI.DbEnv_remove0(swigCPtr, db_home, flags); }
+
+ public void set_cachesize(long bytes, int ncache) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_cachesize(swigCPtr, bytes, ncache); }
+
+ public void set_data_dir(String dir) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_data_dir(swigCPtr, dir); }
+
+ public void set_encrypt(String passwd, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_encrypt(swigCPtr, passwd, flags); }
+
+ public void set_errcall(com.sleepycat.db.ErrorHandler db_errcall_fcn) /* no exception */ {
+ db_javaJNI.DbEnv_set_errcall(swigCPtr, (error_handler = db_errcall_fcn) );
+ }
+
+ public void set_errpfx(String errpfx) /* no exception */ {
+ db_javaJNI.DbEnv_set_errpfx(swigCPtr, errpfx);
+ }
+
+ public void set_flags(int flags, boolean onoff) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_flags(swigCPtr, flags, onoff); }
+
+ public void set_feedback(com.sleepycat.db.FeedbackHandler env_feedback_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_feedback(swigCPtr, (env_feedback_handler = env_feedback_fcn) ); }
+
+ public void set_mp_mmapsize(long mp_mmapsize) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_mp_mmapsize(swigCPtr, mp_mmapsize); }
+
+ public void set_msgcall(com.sleepycat.db.MessageHandler db_msgcall_fcn) /* no exception */ {
+ db_javaJNI.DbEnv_set_msgcall(swigCPtr, (message_handler = db_msgcall_fcn) );
+ }
+
+ public void set_paniccall(com.sleepycat.db.PanicHandler db_panic_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_paniccall(swigCPtr, (panic_handler = db_panic_fcn) ); }
+
+ public void set_rpc_server(DbClient client, String host, long cl_timeout, long sv_timeout, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_rpc_server(swigCPtr, client, host, cl_timeout, sv_timeout, flags); }
+
+ public void set_shm_key(long shm_key) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_shm_key(swigCPtr, shm_key); }
+
+ public void set_tas_spins(int tas_spins) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_tas_spins(swigCPtr, tas_spins); }
+
+ public void set_timeout(long timeout, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_timeout(swigCPtr, timeout, flags); }
+
+ public void set_tmp_dir(String dir) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_tmp_dir(swigCPtr, dir); }
+
+ public void set_tx_max(int max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_tx_max(swigCPtr, max); }
+
+ public void set_app_dispatch(com.sleepycat.db.LogRecordHandler tx_recover) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_app_dispatch(swigCPtr, (app_dispatch_handler = tx_recover) ); }
+
+ /* package */ void set_tx_timestamp0(long timestamp) { db_javaJNI.DbEnv_set_tx_timestamp0(swigCPtr, timestamp); }
+
+ public void set_verbose(int which, boolean onoff) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_verbose(swigCPtr, which, onoff); }
+
+ public byte[][] get_lk_conflicts() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_conflicts(swigCPtr); }
+
+ public int get_lk_detect() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_detect(swigCPtr); }
+
+ public int get_lk_max_locks() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_max_locks(swigCPtr); }
+
+ public int get_lk_max_lockers() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_max_lockers(swigCPtr); }
+
+ public int get_lk_max_objects() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_max_objects(swigCPtr); }
+
+ public int lock_detect(int flags, int atype) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_lock_detect(swigCPtr, flags, atype);
+ }
+
+ public DbLock lock_get(int locker, int flags, com.sleepycat.db.DatabaseEntry object, int lock_mode) throws com.sleepycat.db.DatabaseException {
+ long cPtr = db_javaJNI.DbEnv_lock_get(swigCPtr, locker, flags, object, lock_mode);
+ return (cPtr == 0) ? null : new DbLock(cPtr, true);
+ }
+
+ public int lock_id() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_lock_id(swigCPtr); }
+
+ public void lock_id_free(int id) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_lock_id_free(swigCPtr, id); }
+
+ public void lock_put(DbLock lock) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_lock_put(swigCPtr, DbLock.getCPtr(lock)); }
+
+ public com.sleepycat.db.LockStats lock_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_lock_stat(swigCPtr, flags); }
+
+ public void set_lk_conflicts(byte[][] conflicts) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_conflicts(swigCPtr, conflicts); }
+
+ public void set_lk_detect(int detect) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_detect(swigCPtr, detect); }
+
+ public void set_lk_max_lockers(int max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_max_lockers(swigCPtr, max); }
+
+ public void set_lk_max_locks(int max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_max_locks(swigCPtr, max); }
+
+ public void set_lk_max_objects(int max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_max_objects(swigCPtr, max); }
+
+ public int get_lg_bsize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lg_bsize(swigCPtr); }
+
+ public String get_lg_dir() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_get_lg_dir(swigCPtr);
+ }
+
+ public int get_lg_max() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lg_max(swigCPtr); }
+
+ public int get_lg_regionmax() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lg_regionmax(swigCPtr); }
+
+ public String[] log_archive(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_log_archive(swigCPtr, flags); }
+
+ public static int log_compare(com.sleepycat.db.LogSequenceNumber lsn0, com.sleepycat.db.LogSequenceNumber lsn1) /* no exception */ {
+ return db_javaJNI.DbEnv_log_compare(lsn0, lsn1);
+ }
+
+ public DbLogc log_cursor(int flags) throws com.sleepycat.db.DatabaseException {
+ long cPtr = db_javaJNI.DbEnv_log_cursor(swigCPtr, flags);
+ return (cPtr == 0) ? null : new DbLogc(cPtr, true);
+ }
+
+ public String log_file(com.sleepycat.db.LogSequenceNumber lsn) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_log_file(swigCPtr, lsn);
+ }
+
+ public void log_flush(com.sleepycat.db.LogSequenceNumber lsn) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_log_flush(swigCPtr, lsn); }
+
+ public void log_put(com.sleepycat.db.LogSequenceNumber lsn, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_log_put(swigCPtr, lsn, data, flags); }
+
+ public com.sleepycat.db.LogStats log_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_log_stat(swigCPtr, flags); }
+
+ public void set_lg_bsize(int lg_bsize) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lg_bsize(swigCPtr, lg_bsize); }
+
+ public void set_lg_dir(String dir) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lg_dir(swigCPtr, dir); }
+
+ public void set_lg_max(int lg_max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lg_max(swigCPtr, lg_max); }
+
+ public void set_lg_regionmax(int lg_regionmax) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lg_regionmax(swigCPtr, lg_regionmax); }
+
+ public long get_cachesize() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_get_cachesize(swigCPtr);
+ }
+
+ public int get_cachesize_ncache() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_get_cachesize_ncache(swigCPtr);
+ }
+
+ public long get_mp_mmapsize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_mp_mmapsize(swigCPtr); }
+
+ public com.sleepycat.db.CacheStats memp_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_memp_stat(swigCPtr, flags); }
+
+ public com.sleepycat.db.CacheFileStats[] memp_fstat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_memp_fstat(swigCPtr, flags); }
+
+ public int memp_trickle(int percent) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_memp_trickle(swigCPtr, percent);
+ }
+
+ public int get_tx_max() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_tx_max(swigCPtr); }
+
+ public long get_tx_timestamp() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_tx_timestamp(swigCPtr); }
+
+ public long get_timeout(int flag) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_timeout(swigCPtr, flag); }
+
+ public DbTxn txn_begin(DbTxn parent, int flags) throws com.sleepycat.db.DatabaseException {
+ long cPtr = db_javaJNI.DbEnv_txn_begin(swigCPtr, DbTxn.getCPtr(parent), flags);
+ return (cPtr == 0) ? null : new DbTxn(cPtr, false);
+ }
+
+ public void txn_checkpoint(int kbyte, int min, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_txn_checkpoint(swigCPtr, kbyte, min, flags); }
+
+ public com.sleepycat.db.PreparedTransaction[] txn_recover(int count, int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_txn_recover(swigCPtr, count, flags); }
+
+ public com.sleepycat.db.TransactionStats txn_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_txn_stat(swigCPtr, flags); }
+
+ public long get_rep_limit() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_get_rep_limit(swigCPtr);
+ }
+
+ public int rep_elect(int nsites, int nvotes, int priority, int timeout, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbEnv_rep_elect(swigCPtr, nsites, nvotes, priority, timeout, flags);
+ }
+
+ public int rep_process_message(com.sleepycat.db.DatabaseEntry control, com.sleepycat.db.DatabaseEntry rec, DbEnv.RepProcessMessage envid, com.sleepycat.db.LogSequenceNumber ret_lsn) /* no exception */ {
+ return db_javaJNI.DbEnv_rep_process_message(swigCPtr, control, rec, envid, ret_lsn);
+ }
+
+ public void rep_start(com.sleepycat.db.DatabaseEntry cdata, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_rep_start(swigCPtr, cdata, flags); }
+
+ public com.sleepycat.db.ReplicationStats rep_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_rep_stat(swigCPtr, flags); }
+
+ public void set_rep_limit(long bytes) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_rep_limit(swigCPtr, bytes); }
+
+ public void set_rep_transport(int envid, com.sleepycat.db.ReplicationTransport send) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_rep_transport(swigCPtr, envid, (rep_transport_handler = send) ); }
+
+ public static String strerror(int error) /* no exception */ {
+ return db_javaJNI.DbEnv_strerror(error);
+ }
+
+ public static int get_version_major() /* no exception */ {
+ return db_javaJNI.DbEnv_get_version_major();
+ }
+
+ public static int get_version_minor() /* no exception */ {
+ return db_javaJNI.DbEnv_get_version_minor();
+ }
+
+ public static int get_version_patch() /* no exception */ {
+ return db_javaJNI.DbEnv_get_version_patch();
+ }
+
+ public static String get_version_string() /* no exception */ {
+ return db_javaJNI.DbEnv_get_version_string();
+ }
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/DbLock.java b/db/java/src/com/sleepycat/db/internal/DbLock.java
new file mode 100644
index 000000000..723629908
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbLock.java
@@ -0,0 +1,51 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package com.sleepycat.db.internal;
+
+
+import com.sleepycat.db.*;
+import java.util.Comparator;
+
+public class DbLock {
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ protected DbLock(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ protected DbLock() {
+ this(0, false);
+ }
+
+ protected void finalize() {
+ try {
+ delete();
+ } catch(Exception e) {
+ System.err.println("Exception during finalization: " + e);
+ e.printStackTrace(System.err);
+ }
+ }
+
+ /* package */ void delete() {
+ if(swigCPtr != 0 && swigCMemOwn) {
+ swigCMemOwn = false;
+ db_javaJNI.delete_DbLock(swigCPtr);
+ }
+ swigCPtr = 0;
+ }
+
+ protected static long getCPtr(DbLock obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ public Lock wrapper;
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/DbLogc.java b/db/java/src/com/sleepycat/db/internal/DbLogc.java
new file mode 100644
index 000000000..d26d95f60
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbLogc.java
@@ -0,0 +1,54 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package com.sleepycat.db.internal;
+
+
+import com.sleepycat.db.*;
+import java.util.Comparator;
+
+public class DbLogc {
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ protected DbLogc(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ protected DbLogc() {
+ this(0, false);
+ }
+
+ /* package */ void delete() {
+ if(swigCPtr != 0 && swigCMemOwn) {
+ swigCMemOwn = false;
+ throw new UnsupportedOperationException("C++ destructor does not have public access");
+ }
+ swigCPtr = 0;
+ }
+
+ protected static long getCPtr(DbLogc obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ public synchronized void close(int flags) throws DatabaseException {
+ try {
+ close0(flags);
+ } finally {
+ swigCPtr = 0;
+ }
+ }
+
+ /* package */ void close0(int flags) { db_javaJNI.DbLogc_close0(swigCPtr, flags); }
+
+ public int get(com.sleepycat.db.LogSequenceNumber lsn, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbLogc_get(swigCPtr, lsn, data, flags);
+ }
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/DbMpoolFile.java b/db/java/src/com/sleepycat/db/internal/DbMpoolFile.java
new file mode 100644
index 000000000..e72c1c70d
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbMpoolFile.java
@@ -0,0 +1,56 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package com.sleepycat.db.internal;
+
+
+import com.sleepycat.db.*;
+import java.util.Comparator;
+
+public class DbMpoolFile {
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ protected DbMpoolFile(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ protected DbMpoolFile() {
+ this(0, false);
+ }
+
+ /* package */ void delete() {
+ if(swigCPtr != 0 && swigCMemOwn) {
+ swigCMemOwn = false;
+ throw new UnsupportedOperationException("C++ destructor does not have public access");
+ }
+ swigCPtr = 0;
+ }
+
+ protected static long getCPtr(DbMpoolFile obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ public int get_priority() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbMpoolFile_get_priority(swigCPtr);
+ }
+
+ public void set_priority(int priority) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbMpoolFile_set_priority(swigCPtr, priority); }
+
+ public int get_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbMpoolFile_get_flags(swigCPtr); }
+
+ public void set_flags(int flags, boolean onoff) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbMpoolFile_set_flags(swigCPtr, flags, onoff); }
+
+ public long get_maxsize() throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.DbMpoolFile_get_maxsize(swigCPtr);
+ }
+
+ public void set_maxsize(long bytes) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbMpoolFile_set_maxsize(swigCPtr, bytes); }
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/DbSequence.java b/db/java/src/com/sleepycat/db/internal/DbSequence.java
new file mode 100644
index 000000000..ac8da65b5
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbSequence.java
@@ -0,0 +1,96 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package com.sleepycat.db.internal;
+
+
+import com.sleepycat.db.*;
+import java.util.Comparator;
+
+public class DbSequence {
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ protected DbSequence(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ protected DbSequence() {
+ this(0, false);
+ }
+
+ /* package */ void delete() {
+ if(swigCPtr != 0 && swigCMemOwn) {
+ swigCMemOwn = false;
+ throw new UnsupportedOperationException("C++ destructor does not have public access");
+ }
+ swigCPtr = 0;
+ }
+
+ protected static long getCPtr(DbSequence obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ public Sequence wrapper;
+
+ public synchronized void close(int flags) throws DatabaseException {
+ try {
+ close0(flags);
+ } finally {
+ swigCPtr = 0;
+ }
+ }
+
+ public synchronized void remove(DbTxn txn, int flags)
+ throws DatabaseException {
+ try {
+ remove0(txn, flags);
+ } finally {
+ swigCPtr = 0;
+ }
+ }
+
+ public DbSequence(Db db, int flags) throws com.sleepycat.db.DatabaseException {
+ this(db_javaJNI.new_DbSequence(Db.getCPtr(db), flags), true);
+ }
+
+ /* package */ void close0(int flags) { db_javaJNI.DbSequence_close0(swigCPtr, flags); }
+
+ public long get(DbTxn txnid, int delta, int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get(swigCPtr, DbTxn.getCPtr(txnid), delta, flags); }
+
+ public int get_cachesize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get_cachesize(swigCPtr); }
+
+ public Db get_db() throws com.sleepycat.db.DatabaseException {
+ long cPtr = db_javaJNI.DbSequence_get_db(swigCPtr);
+ return (cPtr == 0) ? null : new Db(cPtr, false);
+ }
+
+ public int get_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get_flags(swigCPtr); }
+
+ public void get_key(com.sleepycat.db.DatabaseEntry key) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_get_key(swigCPtr, key); }
+
+ public long get_range_min() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get_range_min(swigCPtr); }
+
+ public long get_range_max() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get_range_max(swigCPtr); }
+
+ public void initial_value(long val) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_initial_value(swigCPtr, val); }
+
+ public void open(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_open(swigCPtr, DbTxn.getCPtr(txnid), key, flags); }
+
+ /* package */ void remove0(DbTxn txnid, int flags) { db_javaJNI.DbSequence_remove0(swigCPtr, DbTxn.getCPtr(txnid), flags); }
+
+ public void set_cachesize(int size) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_set_cachesize(swigCPtr, size); }
+
+ public void set_flags(int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_set_flags(swigCPtr, flags); }
+
+ public void set_range(long min, long max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_set_range(swigCPtr, min, max); }
+
+ public com.sleepycat.db.SequenceStats stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_stat(swigCPtr, flags); }
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/DbTxn.java b/db/java/src/com/sleepycat/db/internal/DbTxn.java
new file mode 100644
index 000000000..d277f1982
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbTxn.java
@@ -0,0 +1,103 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package com.sleepycat.db.internal;
+
+
+import com.sleepycat.db.*;
+import java.util.Comparator;
+
+public class DbTxn {
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ protected DbTxn(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ protected DbTxn() {
+ this(0, false);
+ }
+
+ /* package */ void delete() {
+ if(swigCPtr != 0 && swigCMemOwn) {
+ swigCMemOwn = false;
+ throw new UnsupportedOperationException("C++ destructor does not have public access");
+ }
+ swigCPtr = 0;
+ }
+
+ protected static long getCPtr(DbTxn obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ public void abort() throws DatabaseException {
+ try {
+ abort0();
+ } finally {
+ swigCPtr = 0;
+ }
+ }
+
+ public void commit(int flags) throws DatabaseException {
+ try {
+ commit0(flags);
+ } finally {
+ swigCPtr = 0;
+ }
+ }
+
+ public void discard(int flags) throws DatabaseException {
+ try {
+ discard0(flags);
+ } finally {
+ swigCPtr = 0;
+ }
+ }
+
+ /*
+ * We override Object.equals because it is possible for the Java API to
+ * create multiple DbTxns that reference the same underlying object.
+ * This can happen for example during DbEnv.txn_recover().
+ */
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+
+ if (obj != null && (obj instanceof DbTxn)) {
+ DbTxn that = (DbTxn)obj;
+ return (this.swigCPtr == that.swigCPtr);
+ }
+ return false;
+ }
+
+ /*
+ * We must override Object.hashCode whenever we override
+ * Object.equals() to enforce the maxim that equal objects have the
+ * same hashcode.
+ */
+ public int hashCode()
+ {
+ return ((int)swigCPtr ^ (int)(swigCPtr >> 32));
+ }
+
+ /* package */ void abort0() { db_javaJNI.DbTxn_abort0(swigCPtr); }
+
+ /* package */ void commit0(int flags) { db_javaJNI.DbTxn_commit0(swigCPtr, flags); }
+
+ /* package */ void discard0(int flags) { db_javaJNI.DbTxn_discard0(swigCPtr, flags); }
+
+ public int id() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbTxn_id(swigCPtr); }
+
+ public void prepare(byte[] gid) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbTxn_prepare(swigCPtr, gid); }
+
+ public void set_timeout(long timeout, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbTxn_set_timeout(swigCPtr, timeout, flags); }
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/DbUtil.java b/db/java/src/com/sleepycat/db/internal/DbUtil.java
new file mode 100644
index 000000000..54a2730f9
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/DbUtil.java
@@ -0,0 +1,179 @@
+/*
+ * -
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2003
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: DbUtil.java,v 1.2 2004/09/22 18:01:04 bostic Exp $
+ */
+package com.sleepycat.db.internal;
+
+/**
+ * DbUtil is a simple class that holds a few static utility functions other
+ * parts of the package share and that don't have a good home elsewhere. (For
+ * now, that's limited to byte-array-to-int conversion and back.)
+ */
+
+public class DbUtil {
+ /**
+ * Get the u_int32_t stored beginning at offset "offset" into
+ * array "arr". We have to do the conversion manually since it's
+ * a C-native int, and we're not really supposed to make this
+ * kind of cast in Java.
+ *
+ * @return Description of the Return Value
+ */
+ public static int array2int(byte[] arr, int offset) {
+ int b1;
+ int b2;
+ int b3;
+ int b4;
+ int pos = offset;
+
+ // Get the component bytes; b4 is most significant, b1 least.
+ if (big_endian) {
+ b4 = arr[pos++];
+ b3 = arr[pos++];
+ b2 = arr[pos++];
+ b1 = arr[pos];
+ } else {
+ b1 = arr[pos++];
+ b2 = arr[pos++];
+ b3 = arr[pos++];
+ b4 = arr[pos];
+ }
+
+ // Bytes are signed. Convert [-128, -1] to [128, 255].
+ if (b1 < 0) {
+ b1 += 256;
+ }
+ if (b2 < 0) {
+ b2 += 256;
+ }
+ if (b3 < 0) {
+ b3 += 256;
+ }
+ if (b4 < 0) {
+ b4 += 256;
+ }
+
+ // Put the bytes in their proper places in an int.
+ b2 <<= 8;
+ b3 <<= 16;
+ b4 <<= 24;
+
+ // Return their sum.
+ return (b1 + b2 + b3 + b4);
+ }
+
+
+ /**
+ * Store the specified u_int32_t, with endianness appropriate to
+ * the platform we're running on, into four consecutive bytes of
+ * the specified byte array, starting from the specified offset.
+ */
+ public static void int2array(int n, byte[] arr, int offset) {
+ int b1;
+ int b2;
+ int b3;
+ int b4;
+ int pos = offset;
+
+ b1 = n & 0xff;
+ b2 = (n >> 8) & 0xff;
+ b3 = (n >> 16) & 0xff;
+ b4 = (n >> 24) & 0xff;
+
+ // Bytes are signed. Convert [128, 255] to [-128, -1].
+ if (b1 >= 128) {
+ b1 -= 256;
+ }
+ if (b2 >= 128) {
+ b2 -= 256;
+ }
+ if (b3 >= 128) {
+ b3 -= 256;
+ }
+ if (b4 >= 128) {
+ b4 -= 256;
+ }
+
+ // Put the bytes in the appropriate place in the array.
+ if (big_endian) {
+ arr[pos++] = (byte) b4;
+ arr[pos++] = (byte) b3;
+ arr[pos++] = (byte) b2;
+ arr[pos] = (byte) b1;
+ } else {
+ arr[pos++] = (byte) b1;
+ arr[pos++] = (byte) b2;
+ arr[pos++] = (byte) b3;
+ arr[pos] = (byte) b4;
+ }
+ }
+
+
+ /**
+ * Convert a byte array to a concise, readable string suitable
+ * for use in toString methods of the *Stat classes.
+ *
+ * @return Description of the Return Value
+ */
+ public static String byteArrayToString(byte[] barr) {
+ if (barr == null) {
+ return "null";
+ }
+
+ StringBuffer sb = new StringBuffer();
+ int len = barr.length;
+ for (int i = 0; i < len; i++) {
+ sb.append('x');
+ int val = (barr[i] >> 4) & 0xf;
+ if (val < 10) {
+ sb.append((char) ('0' + val));
+ } else {
+ sb.append((char) ('a' + val - 10));
+ }
+ val = barr[i] & 0xf;
+ if (val < 10) {
+ sb.append((char) ('0' + val));
+ } else {
+ sb.append((char) ('a' + val - 10));
+ }
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Convert an object array to a string, suitable for use in
+ * toString methods of the *Stat classes.
+ *
+ * @return Description of the Return Value
+ */
+ public static String objectArrayToString(Object[] arr, String name) {
+ if (arr == null) {
+ return "null";
+ }
+
+ StringBuffer sb = new StringBuffer();
+ int len = arr.length;
+ for (int i = 0; i < len; i++) {
+ sb.append("\n " + name + "[" + i + "]:\n");
+ sb.append(" " + arr[i].toString());
+ }
+ return sb.toString();
+ }
+
+ public static int default_lorder() {
+ return big_endian ? 4321 : 1234;
+ }
+
+ private final static boolean big_endian = is_big_endian();
+
+ /**
+ * @return Description of the Return Value
+ */
+ private native static boolean is_big_endian();
+}
diff --git a/db/java/src/com/sleepycat/db/internal/Dbc.java b/db/java/src/com/sleepycat/db/internal/Dbc.java
new file mode 100644
index 000000000..9d9bced88
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/Dbc.java
@@ -0,0 +1,73 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package com.sleepycat.db.internal;
+
+
+import com.sleepycat.db.*;
+import java.util.Comparator;
+
+public class Dbc {
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ protected Dbc(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ protected Dbc() {
+ this(0, false);
+ }
+
+ /* package */ void delete() {
+ if(swigCPtr != 0 && swigCMemOwn) {
+ swigCMemOwn = false;
+ throw new UnsupportedOperationException("C++ destructor does not have public access");
+ }
+ swigCPtr = 0;
+ }
+
+ protected static long getCPtr(Dbc obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ public synchronized void close() throws DatabaseException {
+ try {
+ close0();
+ } finally {
+ swigCPtr = 0;
+ }
+ }
+
+ /* package */ void close0() { db_javaJNI.Dbc_close0(swigCPtr); }
+
+ public int count(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.Dbc_count(swigCPtr, flags); }
+
+ public int del(int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Dbc_del(swigCPtr, flags);
+ }
+
+ public Dbc dup(int flags) throws com.sleepycat.db.DatabaseException {
+ long cPtr = db_javaJNI.Dbc_dup(swigCPtr, flags);
+ return (cPtr == 0) ? null : new Dbc(cPtr, false);
+ }
+
+ public int get(com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Dbc_get(swigCPtr, key, data, flags);
+ }
+
+ public int pget(com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry pkey, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Dbc_pget(swigCPtr, key, pkey, data, flags);
+ }
+
+ public int put(com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException {
+ return db_javaJNI.Dbc_put(swigCPtr, key, data, flags);
+ }
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/db_java.java b/db/java/src/com/sleepycat/db/internal/db_java.java
new file mode 100644
index 000000000..6bfbf64c8
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/db_java.java
@@ -0,0 +1,34 @@
+package com.sleepycat.db.internal;
+
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+
+/* package */ class db_java {
+ public static void DbEnv_lock_vec(DbEnv dbenv, int locker, int flags, com.sleepycat.db.LockRequest[] list, int offset, int nlist) throws com.sleepycat.db.DatabaseException {
+ db_javaJNI.DbEnv_lock_vec(DbEnv.getCPtr(dbenv), locker, flags, list, offset, nlist);
+ }
+
+ /* package */ static long initDbEnvRef0(DbEnv self, Object handle) {
+ return db_javaJNI.initDbEnvRef0(DbEnv.getCPtr(self), handle);
+ }
+
+ /* package */ static long initDbRef0(Db self, Object handle) {
+ return db_javaJNI.initDbRef0(Db.getCPtr(self), handle);
+ }
+
+ /* package */ static void deleteRef0(long ref) {
+ db_javaJNI.deleteRef0(ref);
+ }
+
+ /* package */ static DbEnv getDbEnv0(Db self) {
+ long cPtr = db_javaJNI.getDbEnv0(Db.getCPtr(self));
+ return (cPtr == 0) ? null : new DbEnv(cPtr, false);
+ }
+
+}
diff --git a/db/java/src/com/sleepycat/db/internal/db_javaJNI.java b/db/java/src/com/sleepycat/db/internal/db_javaJNI.java
new file mode 100644
index 000000000..4b47b3bec
--- /dev/null
+++ b/db/java/src/com/sleepycat/db/internal/db_javaJNI.java
@@ -0,0 +1,263 @@
+package com.sleepycat.db.internal;
+
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version: 1.3.21
+ *
+ * Do not make changes to this file unless you know what you are doing--modify
+ * the SWIG interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+
+class db_javaJNI {
+
+ static {
+ /* An alternate library name can be specified via a property. */
+ String libname;
+
+ if ((libname = System.getProperty("sleepycat.db.libfile")) != null)
+ System.load(libname);
+ else if ((libname = System.getProperty("sleepycat.db.libname")) != null)
+ System.loadLibrary(libname);
+ else {
+ String os = System.getProperty("os.name");
+ if (os != null && os.startsWith("Windows")) {
+ /*
+ * On Windows, library name is something like
+ * "libdb_java42.dll" or "libdb_java42d.dll".
+ */
+ libname = "libdb_java" +
+ DbConstants.DB_VERSION_MAJOR +
+ DbConstants.DB_VERSION_MINOR;
+
+ try {
+ System.loadLibrary(libname);
+ } catch (UnsatisfiedLinkError e) {
+ try {
+ libname += "d";
+ System.loadLibrary(libname);
+ } catch (UnsatisfiedLinkError e2) {
+ throw e;
+ }
+ }
+ } else {
+ /*
+ * On UNIX, library name is something like
+ * "libdb_java-3.0.so".
+ */
+ System.loadLibrary("db_java-" +
+ DbConstants.DB_VERSION_MAJOR + "." +
+ DbConstants.DB_VERSION_MINOR);
+ }
+ }
+
+ initialize();
+
+ if (DbEnv_get_version_major() != DbConstants.DB_VERSION_MAJOR ||
+ DbEnv_get_version_minor() != DbConstants.DB_VERSION_MINOR ||
+ DbEnv_get_version_patch() != DbConstants.DB_VERSION_PATCH)
+ throw new RuntimeException("Berkeley DB library version doesn't match Java classes");
+ }
+
+ static native final void initialize();
+
+ public final static native void DbEnv_lock_vec(long jarg1, int jarg2, int jarg3, com.sleepycat.db.LockRequest[] jarg4, int jarg5, int jarg6) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native long initDbEnvRef0(long jarg1, Object jarg2);
+ /* package */ final static native long initDbRef0(long jarg1, Object jarg2);
+ /* package */ final static native void deleteRef0(long jarg1);
+ /* package */ final static native long getDbEnv0(long jarg1);
+ public final static native long new_Db(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_associate(long jarg1, long jarg2, long jarg3, com.sleepycat.db.SecondaryKeyCreator jarg4, int jarg5) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native int Db_close0(long jarg1, int jarg2);
+ public final static native long Db_cursor(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_del(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_err(long jarg1, int jarg2, String jarg3) /* no exception */;
+ public final static native void Db_errx(long jarg1, String jarg2) /* no exception */;
+ public final static native boolean Db_get_transactional(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.DatabaseEntry jarg4, int jarg5) throws com.sleepycat.db.DatabaseException;
+ public final static native boolean Db_get_byteswapped(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long Db_get_cachesize(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_cachesize_ncache(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String Db_get_filename(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String Db_get_dbname(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_encrypt_flags(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String Db_get_errpfx(long jarg1) /* no exception */;
+ public final static native int Db_get_flags(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_lorder(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long Db_get_mpf(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_open_flags(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_pagesize(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_bt_minkey(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_h_ffactor(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_h_nelem(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_re_delim(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_re_len(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_re_pad(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String Db_get_re_source(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_q_extentsize(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_get_type(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long Db_join(long jarg1, Dbc[] jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_key_range(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.KeyRange jarg4, int jarg5) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_open(long jarg1, long jarg2, String jarg3, String jarg4, int jarg5, int jarg6, int jarg7) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException;
+ public final static native int Db_pget(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.DatabaseEntry jarg4, com.sleepycat.db.DatabaseEntry jarg5, int jarg6) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_put(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.DatabaseEntry jarg4, int jarg5) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native void Db_remove0(long jarg1, String jarg2, String jarg3, int jarg4);
+ /* package */ final static native void Db_rename0(long jarg1, String jarg2, String jarg3, String jarg4, int jarg5);
+ public final static native void Db_set_append_recno(long jarg1, com.sleepycat.db.RecordNumberAppender jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_bt_compare(long jarg1, java.util.Comparator jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_bt_maxkey(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_bt_minkey(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_bt_prefix(long jarg1, com.sleepycat.db.BtreePrefixCalculator jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_cachesize(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_dup_compare(long jarg1, java.util.Comparator jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_encrypt(long jarg1, String jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_errpfx(long jarg1, String jarg2) /* no exception */;
+ public final static native void Db_set_feedback(long jarg1, com.sleepycat.db.FeedbackHandler jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_flags(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_h_ffactor(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_h_hash(long jarg1, com.sleepycat.db.Hasher jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_h_nelem(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_lorder(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_pagesize(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_re_delim(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_re_len(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_re_pad(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_re_source(long jarg1, String jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_set_q_extentsize(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native Object Db_stat(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_sync(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native int Db_truncate(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void Db_upgrade(long jarg1, String jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native boolean Db_verify0(long jarg1, String jarg2, String jarg3, java.io.OutputStream jarg4, int jarg5);
+ /* package */ final static native void Dbc_close0(long jarg1);
+ public final static native int Dbc_count(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native int Dbc_del(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native long Dbc_dup(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native int Dbc_get(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException;
+ public final static native int Dbc_pget(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.DatabaseEntry jarg4, int jarg5) throws com.sleepycat.db.DatabaseException;
+ public final static native int Dbc_put(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException;
+ public final static native long new_DbEnv(int jarg1) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native void DbEnv_close0(long jarg1, int jarg2);
+ public final static native void DbEnv_dbremove(long jarg1, long jarg2, String jarg3, String jarg4, int jarg5) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException;
+ public final static native void DbEnv_dbrename(long jarg1, long jarg2, String jarg3, String jarg4, String jarg5, int jarg6) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException;
+ public final static native void DbEnv_err(long jarg1, int jarg2, String jarg3) /* no exception */;
+ public final static native void DbEnv_errx(long jarg1, String jarg2) /* no exception */;
+ public final static native String[] DbEnv_get_data_dirs(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_encrypt_flags(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String DbEnv_get_errpfx(long jarg1) /* no exception */;
+ public final static native int DbEnv_get_flags(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String DbEnv_get_home(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_open_flags(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbEnv_get_shm_key(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_tas_spins(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String DbEnv_get_tmp_dir(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native boolean DbEnv_get_verbose(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_open(long jarg1, String jarg2, int jarg3, int jarg4) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException;
+ /* package */ final static native void DbEnv_remove0(long jarg1, String jarg2, int jarg3);
+ public final static native void DbEnv_set_cachesize(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_data_dir(long jarg1, String jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_encrypt(long jarg1, String jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_errcall(long jarg1, com.sleepycat.db.ErrorHandler jarg2) /* no exception */;
+ public final static native void DbEnv_set_errpfx(long jarg1, String jarg2) /* no exception */;
+ public final static native void DbEnv_set_flags(long jarg1, int jarg2, boolean jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_feedback(long jarg1, com.sleepycat.db.FeedbackHandler jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_mp_mmapsize(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_msgcall(long jarg1, com.sleepycat.db.MessageHandler jarg2) /* no exception */;
+ public final static native void DbEnv_set_paniccall(long jarg1, com.sleepycat.db.PanicHandler jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_rpc_server(long jarg1, DbClient jarg2, String jarg3, long jarg4, long jarg5, int jarg6) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_shm_key(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_tas_spins(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_timeout(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_tmp_dir(long jarg1, String jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_tx_max(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_app_dispatch(long jarg1, com.sleepycat.db.LogRecordHandler jarg2) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native void DbEnv_set_tx_timestamp0(long jarg1, long jarg2);
+ public final static native void DbEnv_set_verbose(long jarg1, int jarg2, boolean jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native byte[][] DbEnv_get_lk_conflicts(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_lk_detect(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_lk_max_locks(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_lk_max_lockers(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_lk_max_objects(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_lock_detect(long jarg1, int jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbEnv_lock_get(long jarg1, int jarg2, int jarg3, com.sleepycat.db.DatabaseEntry jarg4, int jarg5) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_lock_id(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_lock_id_free(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_lock_put(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native com.sleepycat.db.LockStats DbEnv_lock_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lk_conflicts(long jarg1, byte[][] jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lk_detect(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lk_max_lockers(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lk_max_locks(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lk_max_objects(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_lg_bsize(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String DbEnv_get_lg_dir(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_lg_max(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_lg_regionmax(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native String[] DbEnv_log_archive(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_log_compare(com.sleepycat.db.LogSequenceNumber jarg1, com.sleepycat.db.LogSequenceNumber jarg2) /* no exception */;
+ public final static native long DbEnv_log_cursor(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native String DbEnv_log_file(long jarg1, com.sleepycat.db.LogSequenceNumber jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_log_flush(long jarg1, com.sleepycat.db.LogSequenceNumber jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_log_put(long jarg1, com.sleepycat.db.LogSequenceNumber jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException;
+ public final static native com.sleepycat.db.LogStats DbEnv_log_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lg_bsize(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lg_dir(long jarg1, String jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lg_max(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_lg_regionmax(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbEnv_get_cachesize(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_cachesize_ncache(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbEnv_get_mp_mmapsize(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native com.sleepycat.db.CacheStats DbEnv_memp_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native com.sleepycat.db.CacheFileStats[] DbEnv_memp_fstat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_memp_trickle(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_get_tx_max(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbEnv_get_tx_timestamp(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbEnv_get_timeout(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbEnv_txn_begin(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_txn_checkpoint(long jarg1, int jarg2, int jarg3, int jarg4) throws com.sleepycat.db.DatabaseException;
+ public final static native com.sleepycat.db.PreparedTransaction[] DbEnv_txn_recover(long jarg1, int jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native com.sleepycat.db.TransactionStats DbEnv_txn_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbEnv_get_rep_limit(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_rep_elect(long jarg1, int jarg2, int jarg3, int jarg4, int jarg5, int jarg6) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbEnv_rep_process_message(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, com.sleepycat.db.DatabaseEntry jarg3, DbEnv.RepProcessMessage jarg4, com.sleepycat.db.LogSequenceNumber jarg5) /* no exception */;
+ public final static native void DbEnv_rep_start(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native com.sleepycat.db.ReplicationStats DbEnv_rep_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_rep_limit(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbEnv_set_rep_transport(long jarg1, int jarg2, com.sleepycat.db.ReplicationTransport jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native String DbEnv_strerror(int jarg1) /* no exception */;
+ public final static native int DbEnv_get_version_major() /* no exception */;
+ public final static native int DbEnv_get_version_minor() /* no exception */;
+ public final static native int DbEnv_get_version_patch() /* no exception */;
+ public final static native String DbEnv_get_version_string() /* no exception */;
+ /* package */ final static native void delete_DbLock(long jarg1);
+ /* package */ final static native void DbLogc_close0(long jarg1, int jarg2);
+ public final static native int DbLogc_get(long jarg1, com.sleepycat.db.LogSequenceNumber jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbMpoolFile_get_priority(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbMpoolFile_set_priority(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbMpoolFile_get_flags(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbMpoolFile_set_flags(long jarg1, int jarg2, boolean jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbMpoolFile_get_maxsize(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbMpoolFile_set_maxsize(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native long new_DbSequence(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native void DbSequence_close0(long jarg1, int jarg2);
+ public final static native long DbSequence_get(long jarg1, long jarg2, int jarg3, int jarg4) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbSequence_get_cachesize(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbSequence_get_db(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native int DbSequence_get_flags(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbSequence_get_key(long jarg1, com.sleepycat.db.DatabaseEntry jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbSequence_get_range_min(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native long DbSequence_get_range_max(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbSequence_initial_value(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbSequence_open(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native void DbSequence_remove0(long jarg1, long jarg2, int jarg3);
+ public final static native void DbSequence_set_cachesize(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbSequence_set_flags(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbSequence_set_range(long jarg1, long jarg2, long jarg3) throws com.sleepycat.db.DatabaseException;
+ public final static native com.sleepycat.db.SequenceStats DbSequence_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException;
+ /* package */ final static native void DbTxn_abort0(long jarg1);
+ /* package */ final static native void DbTxn_commit0(long jarg1, int jarg2);
+ /* package */ final static native void DbTxn_discard0(long jarg1, int jarg2);
+ public final static native int DbTxn_id(long jarg1) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbTxn_prepare(long jarg1, byte[] jarg2) throws com.sleepycat.db.DatabaseException;
+ public final static native void DbTxn_set_timeout(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException;
+}
diff --git a/db/java/src/com/sleepycat/db/package.html b/db/java/src/com/sleepycat/db/package.html
index 73f58df1d..fe77f9223 100644
--- a/db/java/src/com/sleepycat/db/package.html
+++ b/db/java/src/com/sleepycat/db/package.html
@@ -1,7 +1,29 @@
-<!-- $Id: package.html,v 1.4 2003/10/28 12:49:47 gburd Exp $ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
+<head>
+<!--
+
+ See the file LICENSE for redistribution information.
+
+ Copyright (c) 2002-2004
+ Sleepycat Software. All rights reserved.
+
+ $Id: package.html,v 1.6 2004/08/02 18:52:03 mjc Exp $
+
+-->
+</head>
<body>
-<p>Java API programming notes<br>
-<a href="{@docRoot}../ref/java/program.html" target="_top">[reference guide]</a>
+Berkeley DB Java API<br>
+<a href="{@docRoot}/%2e%2e/ref/toc.html" target="_top">[reference guide]</a> <a href="{@docRoot}/%2e%2e/ref/java/program%2ehtml" target="_top">[Java programming notes]</a>.
+<p>
+This package is a wrapper around the Berkeley DB library. It uses JNI
+to provide access to Berkeley DB, which is implemented in C. That means
+that a shared library or DLL must be available when applications use
+this package.
+<p>
+There are also several utilities provided with Berkeley DB that make
+administrative tasks possible from the command line. For more
+information, see the page <a href="{@docRoot}/../utility/index.html"
+target="_top">Berkeley DB Supporting Utilities</a>.
</body>
</html>
diff --git a/db/java/src/com/sleepycat/util/ExceptionUnwrapper.java b/db/java/src/com/sleepycat/util/ExceptionUnwrapper.java
new file mode 100644
index 000000000..ee7e8ceb4
--- /dev/null
+++ b/db/java/src/com/sleepycat/util/ExceptionUnwrapper.java
@@ -0,0 +1,69 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ExceptionUnwrapper.java,v 1.1 2004/04/09 16:34:10 mark Exp $
+ */
+
+package com.sleepycat.util;
+
+/**
+ * Unwraps nested exceptions by calling the {@link
+ * ExceptionWrapper#getDetail()} method for exceptions that implement the
+ * {@link ExceptionWrapper} interface. Does not currently support the Java 1.4
+ * <code>Throwable.getDetail()</code> method.
+ *
+ * @author Mark Hayes
+ */
+public class ExceptionUnwrapper {
+
+ /**
+ * Unwraps an Exception and returns the underlying Exception, or throws an
+ * Error if the underlying Throwable is an Error.
+ *
+ * @param e is the Exception to unwrap.
+ *
+ * @return the underlying Exception.
+ *
+ * @throws Error if the underlying Throwable is an Error.
+ *
+ * @throws IllegalArgumentException if the underlying Throwable is not an
+ * Exception or an Error.
+ */
+ public static Exception unwrap(Exception e) {
+
+ Throwable t = unwrapAny(e);
+ if (t instanceof Exception) {
+ return (Exception) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ throw new IllegalArgumentException("Not Exception or Error: " + t);
+ }
+ }
+
+ /**
+ * Unwraps an Exception and returns the underlying Throwable.
+ *
+ * @param e is the Exception to unwrap.
+ *
+ * @return the underlying Throwable.
+ */
+ public static Throwable unwrapAny(Throwable e) {
+
+ while (true) {
+ if (e instanceof ExceptionWrapper) {
+ Throwable e2 = ((ExceptionWrapper) e).getDetail();
+ if (e2 == null) {
+ return e;
+ } else {
+ e = e2;
+ }
+ } else {
+ return e;
+ }
+ }
+ }
+}
diff --git a/db/java/src/com/sleepycat/util/ExceptionWrapper.java b/db/java/src/com/sleepycat/util/ExceptionWrapper.java
new file mode 100644
index 000000000..4917fdc2b
--- /dev/null
+++ b/db/java/src/com/sleepycat/util/ExceptionWrapper.java
@@ -0,0 +1,25 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: ExceptionWrapper.java,v 1.1 2004/04/09 16:34:10 mark Exp $
+ */
+
+package com.sleepycat.util;
+
+/**
+ * Interface implemented by exceptions that can contain nested exceptions.
+ *
+ * @author Mark Hayes
+ */
+public interface ExceptionWrapper {
+
+ /**
+ * Returns the nested exception or null if none is present.
+ *
+ * @return the nested exception or null if none is present.
+ */
+ Throwable getDetail();
+}
diff --git a/db/java/src/com/sleepycat/util/FastInputStream.java b/db/java/src/com/sleepycat/util/FastInputStream.java
new file mode 100644
index 000000000..6bad7d79a
--- /dev/null
+++ b/db/java/src/com/sleepycat/util/FastInputStream.java
@@ -0,0 +1,179 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: FastInputStream.java,v 1.2 2004/06/04 18:24:50 mark Exp $
+ */
+
+package com.sleepycat.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A replacement for ByteArrayInputStream that does not synchronize every
+ * byte read.
+ *
+ * <p>This class extends {@link InputStream} and its <code>read()</code>
+ * methods allow it to be used as a standard input stream. In addition, it
+ * provides <code>readFast()</code> methods that are not declared to throw
+ * <code>IOException</code>. <code>IOException</code> is never thrown by this
+ * class.</p>
+ *
+ * @author Mark Hayes
+ */
+public class FastInputStream extends InputStream {
+
+ protected int len;
+ protected int off;
+ protected int mark;
+ protected byte[] buf;
+
+ /**
+ * Creates an input stream.
+ *
+ * @param buffer the data to read.
+ */
+ public FastInputStream(byte[] buffer) {
+
+ buf = buffer;
+ len = buffer.length;
+ }
+
+ /**
+ * Creates an input stream.
+ *
+ * @param buffer the data to read.
+ *
+ * @param offset the byte offset at which to begin reading.
+ *
+ * @param length the number of bytes to read.
+ */
+ public FastInputStream(byte[] buffer, int offset, int length) {
+
+ buf = buffer;
+ off = offset;
+ len = length;
+ }
+
+ // --- begin ByteArrayInputStream compatible methods ---
+
+ public int available() {
+
+ return len - off;
+ }
+
+ public boolean markSupported() {
+
+ return true;
+ }
+
+ public void mark(int pos) {
+
+ mark = pos;
+ }
+
+ public void reset() {
+
+ off = mark;
+ }
+
+ public long skip(long count) {
+
+ int myCount = (int) count;
+ if (myCount + off > len) {
+ myCount = len - off;
+ }
+ off += myCount;
+ return myCount;
+ }
+
+ public int read() throws IOException {
+
+ return readFast();
+ }
+
+ public int read(byte[] toBuf) throws IOException {
+
+ return readFast(toBuf, 0, toBuf.length);
+ }
+
+ public int read(byte[] toBuf, int offset, int length) throws IOException {
+
+ return readFast(toBuf, offset, length);
+ }
+
+ // --- end ByteArrayInputStream compatible methods ---
+
+ /**
+ * Equivalent to <code>read()<code> but does not throw
+ * <code>IOException</code>.
+ * @see #read()
+ */
+ public final int readFast() {
+
+ return (off < len) ? (buf[off++] & 0xff) : (-1);
+ }
+
+ /**
+ * Equivalent to <code>read(byte[])<code> but does not throw
+ * <code>IOException</code>.
+ * @see #read(byte[])
+ */
+ public final int readFast(byte[] toBuf) {
+
+ return readFast(toBuf, 0, toBuf.length);
+ }
+
+ /**
+ * Equivalent to <code>read(byte[],int,int)<code> but does not throw
+ * <code>IOException</code>.
+ * @see #read(byte[],int,int)
+ */
+ public final int readFast(byte[] toBuf, int offset, int length) {
+
+ int avail = len - off;
+ if (avail <= 0) {
+ return -1;
+ }
+ if (length > avail) {
+ length = avail;
+ }
+ for (int i = 0; i < length; i++) {
+ toBuf[offset++] = buf[off++];
+ }
+ return length;
+ }
+
+ /**
+ * Returns the underlying data being read.
+ *
+ * @return the underlying data.
+ */
+ public final byte[] getBufferBytes() {
+
+ return buf;
+ }
+
+ /**
+ * Returns the offset at which data is being read from the buffer.
+ *
+ * @return the offset at which data is being read.
+ */
+ public final int getBufferOffset() {
+
+ return off;
+ }
+
+ /**
+ * Returns the end of the buffer being read.
+ *
+ * @return the end of the buffer.
+ */
+ public final int getBufferLength() {
+
+ return len;
+ }
+}
diff --git a/db/java/src/com/sleepycat/util/FastOutputStream.java b/db/java/src/com/sleepycat/util/FastOutputStream.java
new file mode 100644
index 000000000..a0984bfe4
--- /dev/null
+++ b/db/java/src/com/sleepycat/util/FastOutputStream.java
@@ -0,0 +1,278 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: FastOutputStream.java,v 1.3 2004/07/03 16:15:36 mark Exp $
+ */
+
+package com.sleepycat.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * A replacement for ByteArrayOutputStream that does not synchronize every
+ * byte read.
+ *
+ * <p>This class extends {@link OutputStream} and its <code>write()</code>
+ * methods allow it to be used as a standard output stream. In addition, it
+ * provides <code>writeFast()</code> methods that are not declared to throw
+ * <code>IOException</code>. <code>IOException</code> is never thrown by this
+ * class.</p>
+ *
+ * @author Mark Hayes
+ */
+public class FastOutputStream extends OutputStream {
+
+ public static final int DEFAULT_INIT_SIZE = 100;
+ public static final int DEFAULT_BUMP_SIZE = 100;
+
+ private int len;
+ private int bumpLen;
+ private byte[] buf;
+
+ /**
+ * Creates an output stream with default sizes.
+ */
+ public FastOutputStream() {
+
+ initBuffer(DEFAULT_INIT_SIZE, DEFAULT_BUMP_SIZE);
+ }
+
+ /**
+ * Creates an output stream with a default bump size and a given initial
+ * size.
+ *
+ * @param initialSize the initial size of the buffer.
+ */
+ public FastOutputStream(int initialSize) {
+
+ initBuffer(initialSize, DEFAULT_BUMP_SIZE);
+ }
+
+ /**
+ * Creates an output stream with a given bump size and initial size.
+ *
+ * @param initialSize the initial size of the buffer.
+ *
+ * @param bumpSize the amount to increment the buffer.
+ */
+ public FastOutputStream(int initialSize, int bumpSize) {
+
+ initBuffer(initialSize, bumpSize);
+ }
+
+ /**
+ * Creates an output stream with a given initial buffer and a default
+ * bump size.
+ *
+ * @param buffer the initial buffer; will be owned by this object.
+ */
+ public FastOutputStream(byte[] buffer) {
+
+ buf = buffer;
+ bumpLen = DEFAULT_BUMP_SIZE;
+ }
+
+ /**
+ * Creates an output stream with a given initial buffer and a given
+ * bump size.
+ *
+ * @param buffer the initial buffer; will be owned by this object.
+ *
+ * @param bumpSize the amount to increment the buffer.
+ */
+ public FastOutputStream(byte[] buffer, int bumpSize) {
+
+ buf = buffer;
+ bumpLen = bumpSize;
+ }
+
+ private void initBuffer(int bufferSize, int bumpLen) {
+ buf = new byte[bufferSize];
+ this.bumpLen = bumpLen;
+ }
+
+ // --- begin ByteArrayOutputStream compatible methods ---
+
+ public int size() {
+
+ return len;
+ }
+
+ public void reset() {
+
+ len = 0;
+ }
+
+ public void write(int b) throws IOException {
+
+ writeFast(b);
+ }
+
+ public void write(byte[] fromBuf) throws IOException {
+
+ writeFast(fromBuf);
+ }
+
+ public void write(byte[] fromBuf, int offset, int length)
+ throws IOException {
+
+ writeFast(fromBuf, offset, length);
+ }
+
+ public void writeTo(OutputStream out) throws IOException {
+
+ out.write(buf, 0, len);
+ }
+
+ public String toString() {
+
+ return new String(buf, 0, len);
+ }
+
+ public String toString(String encoding)
+ throws UnsupportedEncodingException {
+
+ return new String(buf, 0, len, encoding);
+ }
+
+ public byte[] toByteArray() {
+
+ byte[] toBuf = new byte[len];
+
+ for (int i = 0; i < len; i++)
+ toBuf[i] = buf[i];
+
+ return toBuf;
+ }
+
+ // --- end ByteArrayOutputStream compatible methods ---
+
+ /**
+ * Equivalent to <code>write(int)<code> but does not throw
+ * <code>IOException</code>.
+ * @see #write(int)
+ */
+ public final void writeFast(int b) {
+
+ if (len + 1 > buf.length)
+ bump(1);
+
+ buf[len++] = (byte) b;
+ }
+
+ /**
+ * Equivalent to <code>write(byte[])<code> but does not throw
+ * <code>IOException</code>.
+ * @see #write(byte[])
+ */
+ public final void writeFast(byte[] fromBuf) {
+
+ int needed = len + fromBuf.length - buf.length;
+ if (needed > 0)
+ bump(needed);
+
+ for (int i = 0; i < fromBuf.length; i++)
+ buf[len++] = fromBuf[i];
+ }
+
+ /**
+ * Equivalent to <code>write(byte[],int,int)<code> but does not throw
+ * <code>IOException</code>.
+ * @see #write(byte[],int,int)
+ */
+ public final void writeFast(byte[] fromBuf, int offset, int length) {
+
+ int needed = len + length - buf.length;
+ if (needed > 0)
+ bump(needed);
+
+ int fromLen = offset + length;
+
+ for (int i = offset; i < fromLen; i++)
+ buf[len++] = fromBuf[i];
+ }
+
+ /**
+ * Copy the buffered data to the given array.
+ *
+ * @param toBuf the buffer to hold a copy of the data.
+ *
+ * @param offset the offset at which to start copying.
+ */
+ public void toByteArray(byte[] toBuf, int offset) {
+
+ int toLen = (toBuf.length > len) ? len : toBuf.length;
+
+ for (int i = offset; i < toLen; i++)
+ toBuf[i] = buf[i];
+ }
+
+ /**
+ * Returns the buffer owned by this object.
+ *
+ * @return the buffer.
+ */
+ public byte[] getBufferBytes() {
+
+ return buf;
+ }
+
+ /**
+ * Returns the offset of the internal buffer.
+ *
+ * @return always zero currently.
+ */
+ public int getBufferOffset() {
+
+ return 0;
+ }
+
+ /**
+ * Returns the length used in the internal buffer, i.e., the offset at
+ * which data will be written next.
+ *
+ * @return the buffer length.
+ */
+ public int getBufferLength() {
+
+ return len;
+ }
+
+ /**
+ * Ensure that at least the given number of bytes are available in the
+ * internal buffer.
+ *
+ * @param sizeNeeded the number of bytes desired.
+ */
+ public void makeSpace(int sizeNeeded) {
+
+ int needed = len + sizeNeeded - buf.length;
+ if (needed > 0)
+ bump(needed);
+ }
+
+ /**
+ * Skip the given number of bytes in the buffer.
+ *
+ * @param sizeAdded number of bytes to skip.
+ */
+ public void addSize(int sizeAdded) {
+
+ len += sizeAdded;
+ }
+
+ private void bump(int needed) {
+
+ byte[] toBuf = new byte[buf.length + needed + bumpLen];
+
+ for (int i = 0; i < len; i++)
+ toBuf[i] = buf[i];
+
+ buf = toBuf;
+ }
+}
diff --git a/db/java/src/com/sleepycat/util/IOExceptionWrapper.java b/db/java/src/com/sleepycat/util/IOExceptionWrapper.java
new file mode 100644
index 000000000..76b409829
--- /dev/null
+++ b/db/java/src/com/sleepycat/util/IOExceptionWrapper.java
@@ -0,0 +1,34 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: IOExceptionWrapper.java,v 1.1 2004/04/09 16:34:10 mark Exp $
+ */
+
+package com.sleepycat.util;
+
+import java.io.IOException;
+
+/**
+ * An IOException that can contain nested exceptions.
+ *
+ * @author Mark Hayes
+ */
+public class IOExceptionWrapper
+ extends IOException implements ExceptionWrapper {
+
+ private Throwable e;
+
+ public IOExceptionWrapper(Throwable e) {
+
+ super(e.getMessage());
+ this.e = e;
+ }
+
+ public Throwable getDetail() {
+
+ return e;
+ }
+}
diff --git a/db/java/src/com/sleepycat/util/RuntimeExceptionWrapper.java b/db/java/src/com/sleepycat/util/RuntimeExceptionWrapper.java
new file mode 100644
index 000000000..f40f1ff7a
--- /dev/null
+++ b/db/java/src/com/sleepycat/util/RuntimeExceptionWrapper.java
@@ -0,0 +1,32 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: RuntimeExceptionWrapper.java,v 1.1 2004/04/09 16:34:10 mark Exp $
+ */
+
+package com.sleepycat.util;
+
+/**
+ * A RuntimeException that can contain nested exceptions.
+ *
+ * @author Mark Hayes
+ */
+public class RuntimeExceptionWrapper extends RuntimeException
+ implements ExceptionWrapper {
+
+ private Throwable e;
+
+ public RuntimeExceptionWrapper(Throwable e) {
+
+ super(e.getMessage());
+ this.e = e;
+ }
+
+ public Throwable getDetail() {
+
+ return e;
+ }
+}
diff --git a/db/java/src/com/sleepycat/util/UtfOps.java b/db/java/src/com/sleepycat/util/UtfOps.java
new file mode 100644
index 000000000..19c8a9ee3
--- /dev/null
+++ b/db/java/src/com/sleepycat/util/UtfOps.java
@@ -0,0 +1,281 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2004
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: UtfOps.java,v 1.2 2004/06/04 18:24:51 mark Exp $
+ */
+
+package com.sleepycat.util;
+
+/**
+ * UTF operations with more flexibility than is provided by DataInput and
+ * DataOutput.
+ *
+ * @author Mark Hayes
+ */
+public class UtfOps {
+
+ private static byte[] EMPTY_BYTES = {};
+ private static String EMPTY_STRING = "";
+
+ /**
+ * Returns the byte length of a null terminated UTF string, not including
+ * the terminator.
+ *
+ * @param bytes the data containing the UTF string.
+ *
+ * @param offset the beginning of the string the measure.
+ *
+ * @throws IndexOutOfBoundsException if no zero terminator is found.
+ *
+ * @return the number of bytes.
+ */
+ public static int getZeroTerminatedByteLength(byte[] bytes, int offset)
+ throws IndexOutOfBoundsException {
+
+ int len = 0;
+ while (bytes[offset++] != 0) {
+ len++;
+ }
+ return len;
+ }
+
+ /**
+ * Returns the byte length of the UTF string that would be created by
+ * converting the given characters to UTF.
+ *
+ * @param chars the characters that would be converted.
+ *
+ * @return the byte length of the equivalent UTF data.
+ */
+ public static int getByteLength(char[] chars) {
+
+ return getByteLength(chars, 0, chars.length);
+ }
+
+ /**
+ * Returns the byte length of the UTF string that would be created by
+ * converting the given characters to UTF.
+ *
+ * @param chars the characters that would be converted.
+ *
+ * @param offset the first character to be converted.
+ *
+ * @param length the number of characters to be converted.
+ *
+ * @return the byte length of the equivalent UTF data.
+ */
+ public static int getByteLength(char[] chars, int offset, int length) {
+
+ int len = 0;
+ length += offset;
+ for (int i = offset; i < length; i++) {
+ int c = chars[i];
+ if ((c >= 0x0001) && (c <= 0x007F)) {
+ len++;
+ } else if (c > 0x07FF) {
+ len += 3;
+ } else {
+ len += 2;
+ }
+ }
+ return len;
+ }
+
+ /**
+ * Returns the number of characters represented by the given UTF string.
+ *
+ * @param bytes the UTF string.
+ *
+ * @return the number of characters.
+ *
+ * @throws IndexOutOfBoundsException if a UTF character sequence at the end
+ * of the data is not complete.
+ *
+ * @throws IllegalArgumentException if an illegal UTF sequence is
+ * encountered.
+ */
+ public static int getCharLength(byte[] bytes)
+ throws IllegalArgumentException, IndexOutOfBoundsException {
+
+ return getCharLength(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Returns the number of characters represented by the given UTF string.
+ *
+ * @param bytes the data containing the UTF string.
+ *
+ * @param offset the first byte to be converted.
+ *
+ * @param length the number of byte to be converted.
+ *
+ * @throws IndexOutOfBoundsException if a UTF character sequence at the end
+ * of the data is not complete.
+ *
+ * @throws IllegalArgumentException if an illegal UTF sequence is
+ * encountered.
+ */
+ public static int getCharLength(byte[] bytes, int offset, int length)
+ throws IllegalArgumentException, IndexOutOfBoundsException {
+
+ int charCount = 0;
+ length += offset;
+ while (offset < length) {
+ switch ((bytes[offset] & 0xff) >> 4) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ offset++;
+ break;
+ case 12: case 13:
+ offset += 2;
+ break;
+ case 14:
+ offset += 3;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ charCount++;
+ }
+ return charCount;
+ }
+
+ /**
+ * Converts byte arrays into character arrays.
+ *
+ * @param bytes the source byte data to convert
+ *
+ * @param byteOffset the offset into the byte array at which
+ * to start the conversion
+ *
+ * @param chars the destination array
+ *
+ * @param charOffset the offset into chars at which to begin the copy
+ *
+ * @param len the amount of information to copy into chars
+ *
+ * @param isByteLen if true then len is a measure of bytes, otherwise
+ * len is a measure of characters
+ *
+ * @throws IndexOutOfBoundsException if a UTF character sequence at the end
+ * of the data is not complete.
+ *
+ * @throws IllegalArgumentException if an illegal UTF sequence is
+ * encountered.
+ */
+ public static int bytesToChars(byte[] bytes, int byteOffset,
+ char[] chars, int charOffset,
+ int len, boolean isByteLen)
+ throws IllegalArgumentException, IndexOutOfBoundsException {
+
+ int char1, char2, char3;
+ len += isByteLen ? byteOffset : charOffset;
+ while ((isByteLen ? byteOffset : charOffset) < len) {
+ char1 = bytes[byteOffset++] & 0xff;
+ switch ((char1 & 0xff) >> 4) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ chars[charOffset++] = (char) char1;
+ break;
+ case 12: case 13:
+ char2 = bytes[byteOffset++];
+ if ((char2 & 0xC0) != 0x80) {
+ throw new IllegalArgumentException();
+ }
+ chars[charOffset++] = (char)(((char1 & 0x1F) << 6) |
+ (char2 & 0x3F));
+ break;
+ case 14:
+ char2 = bytes[byteOffset++];
+ char3 = bytes[byteOffset++];
+ if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
+ throw new IllegalArgumentException();
+ chars[charOffset++] = (char)(((char1 & 0x0F) << 12) |
+ ((char2 & 0x3F) << 6) |
+ ((char3 & 0x3F) << 0));
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+ return byteOffset;
+ }
+
+ /**
+ * Converts character arrays into byte arrays.
+ *
+ * @param chars the source character data to convert
+ *
+ * @param charOffset the offset into the character array at which
+ * to start the conversion
+ *
+ * @param bytes the destination array
+ *
+ * @param byteOffset the offset into bytes at which to begin the copy
+ *
+ * @param charLength the length of characters to copy into bytes
+ */
+ public static void charsToBytes(char[] chars, int charOffset,
+ byte[] bytes, int byteOffset,
+ int charLength) {
+ charLength += charOffset;
+ for (int i = charOffset; i < charLength; i++) {
+ int c = chars[i];
+ if ((c >= 0x0001) && (c <= 0x007F)) {
+ bytes[byteOffset++] = (byte) c;
+ } else if (c > 0x07FF) {
+ bytes[byteOffset++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
+ bytes[byteOffset++] = (byte) (0x80 | ((c >> 6) & 0x3F));
+ bytes[byteOffset++] = (byte) (0x80 | ((c >> 0) & 0x3F));
+ } else {
+ bytes[byteOffset++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
+ bytes[byteOffset++] = (byte) (0x80 | ((c >> 0) & 0x3F));
+ }
+ }
+ }
+
+ /**
+ * Converts byte arrays into strings.
+ *
+ * @param bytes the source byte data to convert
+ *
+ * @param offset the offset into the byte array at which
+ * to start the conversion
+ *
+ * @param length the number of bytes to be converted.
+ *
+ * @return the string.
+ *
+ * @throws IndexOutOfBoundsException if a UTF character sequence at the end
+ * of the data is not complete.
+ *
+ * @throws IllegalArgumentException if an illegal UTF sequence is
+ * encountered.
+ */
+ public static String bytesToString(byte[] bytes, int offset, int length)
+ throws IllegalArgumentException, IndexOutOfBoundsException {
+
+ if (length == 0) return EMPTY_STRING;
+ int charLen = UtfOps.getCharLength(bytes, offset, length);
+ char[] chars = new char[charLen];
+ UtfOps.bytesToChars(bytes, offset, chars, 0, length, true);
+ return new String(chars, 0, charLen);
+ }
+
+ /**
+ * Converts strings to byte arrays.
+ *
+ * @param string the string to convert.
+ *
+ * @return the UTF byte array.
+ */
+ public static byte[] stringToBytes(String string) {
+
+ if (string.length() == 0) return EMPTY_BYTES;
+ char[] chars = string.toCharArray();
+ byte[] bytes = new byte[UtfOps.getByteLength(chars)];
+ UtfOps.charsToBytes(chars, 0, bytes, 0, chars.length);
+ return bytes;
+ }
+}
diff --git a/db/java/src/com/sleepycat/util/package.html b/db/java/src/com/sleepycat/util/package.html
new file mode 100644
index 000000000..83fae5ca9
--- /dev/null
+++ b/db/java/src/com/sleepycat/util/package.html
@@ -0,0 +1,6 @@
+<!-- $Id: package.html,v 1.1 2004/08/02 18:52:06 mjc Exp $ -->
+<html>
+<body>
+General utilities used throughout Berkeley DB.
+</body>
+</html>