summaryrefslogtreecommitdiff
path: root/test/scr024
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-10-30 16:00:08 -0700
committerAnas Nashif <anas.nashif@intel.com>2012-10-30 16:00:08 -0700
commit7edf6e8ac0df452d4af7a15da08609821b0b3c0f (patch)
tree1cf0f01d9b6574972173e3cd40b62e4ebeaaaaae /test/scr024
downloaddb4-7edf6e8ac0df452d4af7a15da08609821b0b3c0f.tar.gz
db4-7edf6e8ac0df452d4af7a15da08609821b0b3c0f.tar.bz2
db4-7edf6e8ac0df452d4af7a15da08609821b0b3c0f.zip
Imported Upstream version 4.8.30.NCupstream/4.8.30.NC
Diffstat (limited to 'test/scr024')
-rw-r--r--test/scr024/Makefile33
-rw-r--r--test/scr024/README51
-rw-r--r--test/scr024/build.xml415
-rw-r--r--test/scr024/chk.bdb81
-rw-r--r--test/scr024/src/com/sleepycat/bind/serial/test/MarshalledObject.java127
-rw-r--r--test/scr024/src/com/sleepycat/bind/serial/test/NullClassCatalog.java37
-rw-r--r--test/scr024/src/com/sleepycat/bind/serial/test/SerialBindingTest.java330
-rw-r--r--test/scr024/src/com/sleepycat/bind/serial/test/TestClassCatalog.java56
-rw-r--r--test/scr024/src/com/sleepycat/bind/test/BindingSpeedTest.java484
-rw-r--r--test/scr024/src/com/sleepycat/bind/tuple/test/MarshalledObject.java136
-rw-r--r--test/scr024/src/com/sleepycat/bind/tuple/test/TupleBindingTest.java426
-rw-r--r--test/scr024/src/com/sleepycat/bind/tuple/test/TupleFormatTest.java927
-rw-r--r--test/scr024/src/com/sleepycat/bind/tuple/test/TupleOrderingTest.java477
-rw-r--r--test/scr024/src/com/sleepycat/collections/KeyRangeTest.java440
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/CollectionTest.java3048
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/DbTestUtil.java129
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/ForeignKeyTest.java342
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/IterDeadlockTest.java228
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/JoinTest.java225
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/NullTransactionRunner.java32
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/SecondaryDeadlockTest.java206
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TestDataBinding.java33
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TestEntity.java44
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TestEntityBinding.java63
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TestEnv.java130
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TestKeyAssigner.java41
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TestKeyCreator.java56
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TestSR15721.java119
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TestStore.java279
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/TransactionTest.java838
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/serial/CatalogCornerCaseTest.java97
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTest.java177
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTestInit.java154
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java70
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java.original72
-rw-r--r--test/scr024/src/com/sleepycat/collections/test/serial/TupleSerialFactoryTest.java245
-rw-r--r--test/scr024/src/com/sleepycat/db/util/DualTestCase.java88
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/BindingTest.java2425
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/Enhanced0.java36
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/Enhanced1.java260
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/Enhanced2.java110
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/Enhanced3.java176
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/EvolveCase.java205
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/EvolveClasses.java6818
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/EvolveClasses.java.original2855
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/EvolveTest.java255
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/EvolveTestBase.java438
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/EvolveTestInit.java53
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/ForeignKeyTest.java329
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/IndexTest.java874
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/JoinTest.java176
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/NegativeTest.java644
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/OperationTest.java1552
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/PersistTestUtils.java49
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/SequenceTest.java469
-rw-r--r--test/scr024/src/com/sleepycat/persist/test/SubclassIndexTest.java251
-rw-r--r--test/scr024/src/com/sleepycat/util/test/ExceptionWrapperTest.java134
-rw-r--r--test/scr024/src/com/sleepycat/util/test/FastOutputStreamTest.java66
-rw-r--r--test/scr024/src/com/sleepycat/util/test/PackedIntegerTest.java191
-rw-r--r--test/scr024/src/com/sleepycat/util/test/SharedTestUtils.java178
-rw-r--r--test/scr024/src/com/sleepycat/util/test/TestEnv.java142
-rw-r--r--test/scr024/src/com/sleepycat/util/test/TxnTestCase.java230
-rw-r--r--test/scr024/src/com/sleepycat/util/test/UtfTest.java163
63 files changed, 29815 insertions, 0 deletions
diff --git a/test/scr024/Makefile b/test/scr024/Makefile
new file mode 100644
index 00000000..fe1d0c32
--- /dev/null
+++ b/test/scr024/Makefile
@@ -0,0 +1,33 @@
+TESTCLASSES=\
+ ./src/com/sleepycat/bind/serial/test/*.java\
+ ./src/com/sleepycat/bind/test/*.java\
+ ./src/com/sleepycat/bind/tuple/test/*.java\
+ ./src/com/sleepycat/collections/test/*.java\
+ ./src/com/sleepycat/collections/test/serial/*.java\
+ ./src/com/sleepycat/util/test/*.java \
+ ./src/com/sleepycat/db/util/*.java
+
+TESTSERIALPATH=com/sleepycat/collections/test/serial/TestSerial
+
+all: dbtest.jar
+
+dbtest.jar: classesdir
+ # Compile the tests and build the test jar
+ javac -classpath "${DB_JAR}${CP_SEP}${REQUIRED_JARS}" \
+ -d ./classes ${TESTCLASSES}
+ jar cf ./dbtest.jar -C ./classes ./com/sleepycat
+ # Build the original version of TestSerial in the testserial directory
+ mkdir -p "testserial/${TESTSERIALPATH}"
+ cp "./src/${TESTSERIALPATH}.java.original" \
+ "./testserial/${TESTSERIALPATH}.java"
+ javac -classpath "${DB_JAR}${CP_SEP}${REQUIRED_JARS}" \
+ -d ./testserial "testserial/${TESTSERIALPATH}.java"
+
+classesdir:
+ [ -d ./classes ] || (mkdir ./classes)
+
+clean:
+ [ -d ./classes ] && rm -rf ./classes
+ [ -f ./dbtest.jar ] && rm ./dbtest.jar
+ [ -d ./testserial ] && rm -rf ./testserial
+
diff --git a/test/scr024/README b/test/scr024/README
new file mode 100644
index 00000000..1053cf67
--- /dev/null
+++ b/test/scr024/README
@@ -0,0 +1,51 @@
+This directory contains unit tests for the Java APIs that are common the BDB
+and BDB JE products: the bind, collections, persist (DPL) and util packages.
+
+These tests are run using Apache Ant and JUnit.
+
+External software requirements:
+
+ Java 1.5 or later.
+ The Sun JDK is normally used, but any compatible JVM should work.
+
+ Apache Ant 7.0 or later.
+ http://ant.apache.org/bindownload.cgi
+
+ JUnit 3.8.1 or 3.8.2 (later versions do not currently work)
+ http://prdownloads.sourceforge.net/junit/junit3.8.1.zip?download
+
+The bin directories of both Java and Ant must be in your executable path. The
+junit.jar file must be available to Ant. The simplest way to do this is to copy
+the junit.jar file into your Ant's lib directory.
+
+There are two configuration settings for specifying the DB release to be
+tested:
+
+ The release binaries must be in your system library path (LD_LIBRARY_PATH
+ on Unix).
+
+ The Java property db.jar is specified to override the default location,
+ which is the build_unix/db.jar file in this release directory.
+
+The following ant commands should be executed from this directory:
+
+ # To perform a clean build of the tests
+ ant clean build
+
+ # To build (if needed) and run the tests
+ ant test
+
+ # To test the examples
+ ant examples
+
+For example, here are commands to build and test in a local db directory.
+
+ cd ~/db/test/scr024
+ ant clean build
+ ant test
+
+Several output directories are creating when building and testing. These can
+be deleted with:
+
+ ant clean
+
diff --git a/test/scr024/build.xml b/test/scr024/build.xml
new file mode 100644
index 00000000..472ea0da
--- /dev/null
+++ b/test/scr024/build.xml
@@ -0,0 +1,415 @@
+<!-- $Id$ -->
+<project name="clover" default="build" basedir=".">
+
+ <property name="db" location="../.."/>
+ <property name="db.jar" location="${db}/build_unix/db.jar"/>
+ <property name="test.src" location="src"/>
+ <property name="examples.src" location="${db}/examples_java/src"/>
+ <property name="clover.initstring" location="reports/clover.db"/>
+ <property name="clover.excludes" value="**/test/** collections/** db/** com/sleepycat/db/**"/>
+ <!--
+ <property name="build.compiler"
+ value="org.apache.tools.ant.taskdefs.CloverCompilerAdapter"/>
+ -->
+
+ <target name="all" depends="clean,test,report"/>
+
+ <target name="clean">
+ <delete dir="classes"/>
+ <delete dir="tmp"/>
+ <delete dir="reports"/>
+ <delete dir="original"/>
+ <delete dir="evolved"/>
+ <delete dir="testevolvedir"/>
+ <delete dir="testserialdir"/>
+ <delete dir="testenhancedir"/>
+ </target>
+
+ <target name="init">
+ <mkdir dir="classes"/>
+ <mkdir dir="tmp"/>
+ <mkdir dir="reports"/>
+ </target>
+
+ <path id="clover.classpath">
+ <pathelement location="clover.jar"/>
+ <pathelement location="velocity.jar"/>
+ </path>
+
+ <path id="classpath">
+ <pathelement location="${db.jar}"/>
+ <pathelement location="classes"/>
+ <pathelement location="clover.jar"/>
+ <path refid="clover.classpath"/>
+ </path>
+
+ <path id="enhanced.classpath">
+ <pathelement location="${db.jar}"/>
+ <pathelement location="testenhancedir"/>
+ </path>
+
+ <target name="build" depends="init">
+ <echo message="Using db.jar: ${db.jar}"/>
+ <javac destdir="classes" debug="on" source="1.5" target="1.5">
+ <classpath refid="classpath"/>
+ <src path="${test.src}"/>
+ <src path="${examples.src}"/>
+ <exclude name="com/sleepycat/**/release/**"/>
+ </javac>
+ <!-- Compile original version of TestSerial.java.original. -->
+ <property name="testserialpath"
+ value="com/sleepycat/collections/test/serial/TestSerial"/>
+ <copy file="${test.src}/${testserialpath}.java.original"
+ tofile="testserialdir/${testserialpath}.java"/>
+ <javac destdir="testserialdir" debug="on" source="1.5" target="1.5"
+ includeAntRuntime="true" srcdir="testserialdir">
+ <include name="${testserialpath}.java"/>
+ <classpath refid="classpath"/>
+ </javac>
+ <!-- Compile original version of EvolveClasses. -->
+ <copy file=
+ "${test.src}/com/sleepycat/persist/test/EvolveClasses.java.original"
+ tofile=
+ "testevolvedir/com/sleepycat/persist/test/EvolveClasses.java"/>
+ <copy file=
+ "${test.src}/com/sleepycat/persist/test/EvolveCase.java"
+ tofile=
+ "testevolvedir/com/sleepycat/persist/test/EvolveCase.java"/>
+ <copy file=
+ "${test.src}/com/sleepycat/persist/test/PersistTestUtils.java"
+ tofile=
+ "testevolvedir/com/sleepycat/persist/test/PersistTestUtils.java"/>
+ <javac debug="on" source="1.5" target="1.5">
+ <src path="testevolvedir"/>
+ <classpath refid="classpath"/>
+ </javac>
+ </target>
+
+ <target name="test" depends="build">
+
+ <!-- Determine which tests to run. -->
+ <condition property="dotestserial">
+ <or>
+ <not><isset property="testcase"/></not>
+ <equals arg1="${testcase}" arg2=
+ "com.sleepycat.collections.test.serial.StoredClassCatalogTest"/>
+ </or>
+ </condition>
+ <condition property="dotestevolve">
+ <or>
+ <not><isset property="testcase"/></not>
+ <equals arg1="${testcase}"
+ arg2="com.sleepycat.persist.test.EvolveTest"/>
+ </or>
+ </condition>
+
+ <!-- Performs initialization needed before StoredClassCatalogTest. -->
+ <junit fork="yes" dir="." printsummary="on" haltonfailure="on"
+ showoutput="on">
+ <jvmarg value="-ea"/>
+ <classpath path="testserialdir"/> <!-- Must be first -->
+ <classpath refid="classpath"/>
+ <sysproperty key="testdestdir" value="./tmp"/>
+ <sysproperty key="longtest" value="${longtest}"/>
+ <formatter type="plain" usefile="false"/>
+ <formatter type="xml"/>
+ <test name=
+ "com.sleepycat.collections.test.serial.StoredClassCatalogTestInit"
+ todir="reports" if="dotestserial"/>
+ </junit>
+
+ <!-- Performs initialization needed before persist EvolveTest. -->
+ <junit fork="yes" dir="." printsummary="on" haltonfailure="on"
+ showoutput="on">
+ <jvmarg value="-ea"/>
+ <classpath path="testevolvedir"/>
+ <classpath refid="classpath"/>
+ <sysproperty key="testdestdir" value="./tmp"/>
+ <sysproperty key="longtest" value="${longtest}"/>
+ <formatter type="plain" usefile="false"/>
+ <formatter type="xml"/>
+ <test name="com.sleepycat.persist.test.EvolveTestInit"
+ todir="reports" if="dotestevolve"/>
+ </junit>
+
+ <!-- Performs testcase if set, or batch tests. -->
+ <junit fork="yes" dir="." printsummary="on" haltonfailure="on"
+ showoutput="on">
+ <jvmarg value="-ea"/>
+ <classpath refid="classpath"/>
+ <sysproperty key="testdestdir" value="./tmp"/>
+ <sysproperty key="longtest" value="${longtest}"/>
+ <formatter type="plain" usefile="false"/>
+ <formatter type="xml"/>
+ <test name="${testcase}" todir="reports" if="testcase"/>
+ <batchtest todir="reports" unless="testcase">
+ <fileset dir="classes" includes="**/*Test.class"/>
+ </batchtest>
+ </junit>
+
+ <!-- Run the persist tests with -javaagent to enhance classes as they are
+ loaded. In this, and the following two tests with enhanced classes,
+ set the expectEnhanced system property to cause the test to fail if
+ classes are not enhanced. -->
+ <echo message="Run persist tests with -javaagent"/>
+ <junit fork="yes" dir="." printsummary="on" haltonfailure="on"
+ showoutput="on">
+ <jvmarg value="-ea"/>
+ <jvmarg value="-javaagent:${db.jar}=enhance:-v,com.sleepycat.persist"/>
+ <classpath refid="classpath"/>
+ <sysproperty key="testdestdir" value="./tmp"/>
+ <sysproperty key="longtest" value="${longtest}"/>
+ <sysproperty key="expectEnhanced" value="true"/>
+ <formatter type="plain" usefile="false"/>
+ <formatter type="xml"/>
+ <batchtest todir="reports" unless="testcase">
+ <fileset dir="classes"
+ includes="com/sleepycat/persist/**/*Test.class"/>
+ </batchtest>
+ </junit>
+
+ <!-- Enhance persist test classes by rewriting the class files with the
+ enhancer ant task, and run the persist tests again. Compile
+ ClassEnhancerTask here. It is not compiled into db.jar because that
+ would create a primary build dependency on the Ant libraries. -->
+ <echo message="Run persist tests with ClassEnhancer ant task"/>
+ <delete dir="testenhancedir"/>
+ <mkdir dir="testenhancedir"/>
+ <copy todir="testenhancedir">
+ <fileset dir="classes" includes="com/sleepycat/persist/**/*.class"/>
+ </copy>
+ <javac destdir="testenhancedir" debug="on" source="1.5" target="1.5"
+ includeAntRuntime="true" srcdir="${db}/java/src">
+ <include name="com/sleepycat/persist/model/ClassEnhancerTask.java"/>
+ <classpath refid="classpath"/>
+ </javac>
+ <taskdef name="enhancer"
+ classname="com.sleepycat.persist.model.ClassEnhancerTask">
+ <classpath refid="enhanced.classpath"/>
+ </taskdef>
+ <enhancer verbose="on">
+ <fileset dir="testenhancedir"/>
+ </enhancer>
+ <junit fork="yes" dir="." printsummary="on" haltonfailure="on"
+ showoutput="on">
+ <jvmarg value="-ea"/>
+ <classpath refid="enhanced.classpath"/>
+ <classpath refid="classpath"/>
+ <sysproperty key="testdestdir" value="./tmp"/>
+ <sysproperty key="longtest" value="${longtest}"/>
+ <sysproperty key="expectEnhanced" value="true"/>
+ <formatter type="plain" usefile="false"/>
+ <formatter type="xml"/>
+ <batchtest todir="reports" unless="testcase">
+ <fileset dir="classes"
+ includes="com/sleepycat/persist/**/*Test.class"/>
+ </batchtest>
+ </junit>
+
+ <!-- Enhance persist test classes by rewriting the class files with the
+ ClassEnhancer main program, and run the persist tests again. -->
+ <echo message="Run persist tests with ClassEnhancer main program"/>
+ <delete dir="testenhancedir"/>
+ <mkdir dir="testenhancedir"/>
+ <copy todir="testenhancedir">
+ <fileset dir="classes" includes="com/sleepycat/persist/**/*.class"/>
+ </copy>
+ <java fork="yes" failonerror="true"
+ classname="com.sleepycat.persist.model.ClassEnhancer">
+ <arg line="-v testenhancedir"/>
+ <classpath refid="classpath"/>
+ </java>
+ <junit fork="yes" dir="." printsummary="on" haltonfailure="on"
+ showoutput="on">
+ <jvmarg value="-ea"/>
+ <classpath refid="enhanced.classpath"/>
+ <classpath refid="classpath"/>
+ <sysproperty key="testdestdir" value="./tmp"/>
+ <sysproperty key="longtest" value="${longtest}"/>
+ <sysproperty key="expectEnhanced" value="true"/>
+ <formatter type="plain" usefile="false"/>
+ <formatter type="xml"/>
+ <batchtest todir="reports" unless="testcase">
+ <fileset dir="classes"
+ includes="com/sleepycat/persist/**/*Test.class"/>
+ </batchtest>
+ </junit>
+
+ </target>
+
+ <!-- examples runs all examples, but does not include access_example because
+ it is interactive.
+ -->
+ <target name="examples" depends="build">
+ <echo message="=== HelloDatabaseWorld ==="/>
+ <java dir="." fork="yes" classpathref="classpath" failonerror="true"
+ classname="collections.hello.HelloDatabaseWorld"/>
+ <echo message=""/>
+ <antcall target="one_shipment_example">
+ <param name="param_name" value="basic"/>
+ </antcall>
+ <antcall target="one_shipment_example">
+ <param name="param_name" value="index"/>
+ </antcall>
+ <antcall target="one_shipment_example">
+ <param name="param_name" value="entity"/>
+ </antcall>
+ <antcall target="one_shipment_example">
+ <param name="param_name" value="tuple"/>
+ </antcall>
+ <antcall target="one_shipment_example">
+ <param name="param_name" value="sentity"/>
+ </antcall>
+ <antcall target="one_shipment_example">
+ <param name="param_name" value="marshal"/>
+ </antcall>
+ <antcall target="one_shipment_example">
+ <param name="param_name" value="factory"/>
+ </antcall>
+ <antcall target="one_persist_example">
+ <param name="param_name" value="CustomKeyOrderExample"/>
+ </antcall>
+ <antcall target="one_persist_example">
+ <param name="param_name" value="EventExample"/>
+ </antcall>
+ <antcall target="one_persist_example">
+ <param name="param_name" value="EventExampleDPL"/>
+ </antcall>
+ <antcall target="one_persist_example">
+ <param name="param_name" value="PersonExample"/>
+ </antcall>
+ <antcall target="DplDump">
+ <param name="home" value="tmp"/>
+ <param name="store" value="PersonStore"/>
+ </antcall>
+ <antcall target="db-gettingStarted"/>
+ <antcall target="db-txn"/>
+ <antcall target="persist-gettingStarted"/>
+ <antcall target="persist-txn"/>
+ </target>
+
+ <target name="one_shipment_example">
+ <echo message="=== ${param_name} ==="/>
+ <delete dir="tmp"/>
+ <mkdir dir="tmp"/>
+ <java dir="." fork="yes" classpathref="classpath" failonerror="true"
+ classname="collections.ship.${param_name}.Sample"/>
+ </target>
+
+ <target name="one_persist_example">
+ <echo message="=== ${param_name} ==="/>
+ <delete dir="tmp"/>
+ <mkdir dir="tmp"/>
+ <java fork="yes" dir="." classname="persist.${param_name}"
+ failonerror="true">
+ <jvmarg value="-ea"/>
+ <arg line="-h tmp"/>
+ <classpath refid="classpath"/>
+ </java>
+ </target>
+
+ <!--
+ Before running this example, first run another DPL example that writes to the
+ home directory. Note that we do not clear the home directory here, in order
+ to use the DB in the home directory from a previous run.
+ -->
+ <target name="DplDump">
+ <echo message="=== DplDump ${home} ${store} ==="/>
+ <java fork="yes" dir="." classname="persist.DplDump" failonerror="true">
+ <jvmarg value="-ea"/>
+ <arg line="-h ${home} -s ${store}"/>
+ <classpath refid="classpath"/>
+ </java>
+ </target>
+
+ <target name="access_example" depends="build">
+ <echo message="=== AccessExample ==="/>
+ <java fork="yes" dir="." classpathref="classpath" failonerror="true"
+ classname="collections.access.AccessExample">
+ </java>
+ </target>
+
+ <!-- Test GSG examples -->
+
+ <target name="db-gettingStarted" depends="build">
+ <delete dir="tmp"/>
+ <mkdir dir="tmp"/>
+ <copy todir="tmp">
+ <fileset dir="${examples.src}/db/GettingStarted">
+ <include name="*.txt"/>
+ <exclude name="*.java"/>
+ </fileset>
+ </copy>
+ <java fork="yes" dir="tmp"
+ classname="db.GettingStarted.ExampleDatabaseLoad"
+ failonerror="true">
+ <arg line="-h ."/>
+ <classpath refid="classpath"/>
+ </java>
+ <java fork="yes" dir="tmp"
+ classname="db.GettingStarted.ExampleDatabaseRead"
+ failonerror="true">
+ <arg line="-h ."/>
+ <classpath refid="classpath"/>
+ </java>
+ <delete file="tmp/inventory.txt"/>
+ <delete file="tmp/vendors.txt"/>
+ </target>
+
+ <target name="db-txn" depends="build">
+ <delete dir="tmp"/>
+ <mkdir dir="tmp"/>
+ <java fork="yes" dir="."
+ classname="db.txn.TxnGuide"
+ failonerror="true">
+ <arg line="-h tmp"/>
+ <classpath refid="classpath"/>
+ </java>
+ </target>
+
+ <target name="persist-gettingStarted" depends="build">
+ <delete dir="tmp"/>
+ <mkdir dir="tmp"/>
+ <mkdir dir="tmp/JEDB"/>
+ <copy todir="tmp">
+ <fileset dir="${examples.src}/persist/gettingStarted">
+ <include name="*.txt"/>
+ <exclude name="*.java"/>
+ </fileset>
+ </copy>
+ <java fork="yes" dir="tmp"
+ classname="persist.gettingStarted.SimpleStorePut"
+ failonerror="true">
+ <arg line="-h ."/>
+ <classpath refid="classpath"/>
+ </java>
+ <java fork="yes" dir="tmp"
+ classname="persist.gettingStarted.SimpleStoreGet"
+ failonerror="true">
+ <arg line="-h ."/>
+ <classpath refid="classpath"/>
+ </java>
+ <delete file="tmp/inventory.txt"/>
+ <delete file="tmp/vendors.txt"/>
+ </target>
+
+ <target name="persist-txn" depends="build">
+ <delete dir="tmp"/>
+ <mkdir dir="tmp"/>
+ <java fork="yes" dir="."
+ classname="persist.txn.TxnGuideDPL"
+ failonerror="true">
+ <arg line="-h tmp"/>
+ <classpath refid="classpath"/>
+ </java>
+ </target>
+
+ <target name="report">
+ <java classname="com.cortexeb.tools.clover.reporters.html.HtmlReporter"
+ failonerror="true" fork="true">
+ <arg line="--outputdir reports --showSrc --initstring ${clover.initstring} --title 'Berkeley DB Java BDB API'"/>
+ <classpath refid="clover.classpath"/>
+ </java>
+ </target>
+
+</project>
diff --git a/test/scr024/chk.bdb b/test/scr024/chk.bdb
new file mode 100644
index 00000000..33535258
--- /dev/null
+++ b/test/scr024/chk.bdb
@@ -0,0 +1,81 @@
+#!/bin/sh -
+#
+# $Id$
+#
+# Run the collections/bind test suite.
+
+# NOTES:
+# This test requires one JAR not included with the Berkeley DB
+# distribution: JUnit (junit.jar) I've been using the 8/31/2002 version
+# of JUnit. You can download this JAR from http://jakarta.apache.org/
+#
+# JUNIT_JAR=/Users/gburd/Unix/opt/junit/junit.jar
+
+[ "x$JUNIT_JAR" = "x" ] && {
+ echo 'FAIL: unset environment variable JUNIT_JAR for junit.jar.'
+ exit 1
+}
+
+[ -f $JUNIT_JAR ] || {
+ echo 'FAIL: JUNIT_JAR not a valid path to the junit.jar.'
+ exit 1
+}
+
+case `uname` in
+ *CYGWIN*)
+ d=../../build_windows/Win32/Debug
+ DB_LIB_DIR="$d"
+ REQUIRED_JARS="`cygpath -m $JUNIT_JAR`"
+ CP_SEP=";"
+ PATH="../../build_windows/Win32/Debug:$PATH"
+ export PATH;;
+ *)
+ d=../../build_unix
+ REQUIRED_JARS=$JUNIT_JAR
+ DB_LIB_DIR="$d/.libs"
+ CP_SEP=":"
+ ;;
+esac
+TESTDESTDIR=${TESTDESTDIR:-testdestdir}
+DB_JAR="$d/db.jar"
+export DB_JAR
+export REQUIRED_JARS
+export CP_SEP
+
+# Build the tests.
+
+make clean
+
+[ -f ./dbtest.jar ] || (make dbtest.jar) || {
+ echo 'FAIL: unable to find or build dbtest.jar'
+ exit 1
+}
+
+# Perform initialization needed before StoredClassCatalogTest is run in
+# the tests below. The testserial directory must be first in the classpath.
+
+c="com.sleepycat.collections.test.serial.StoredClassCatalogTestInit"
+echo "Running: $c"
+if java -Djava.library.path=$DB_LIB_DIR -Dtestdestdir=$TESTDESTDIR \
+ -cp "testserial${CP_SEP}$REQUIRED_JARS${CP_SEP}$DB_JAR${CP_SEP}./dbtest.jar" $c ; then
+ :
+else
+ echo "FAIL: test program failed"
+ exit 1
+fi
+
+# Run the tests.
+
+for f in `find classes -name "*Test.class"`; do
+ c=`echo "$f" | sed -e 's/classes\///' -e 's/\.class//' -e 's/\//./g'`
+ echo "Running: $c"
+ if java -Djava.library.path=$DB_LIB_DIR -Dtestdestdir=$TESTDESTDIR\
+ -cp $REQUIRED_JARS${CP_SEP}$DB_JAR${CP_SEP}./dbtest.jar $c ; then
+ :
+ else
+ echo "FAIL: test program failed"
+ exit 1
+ fi
+done
+
+exit 0
diff --git a/test/scr024/src/com/sleepycat/bind/serial/test/MarshalledObject.java b/test/scr024/src/com/sleepycat/bind/serial/test/MarshalledObject.java
new file mode 100644
index 00000000..b03584ae
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/serial/test/MarshalledObject.java
@@ -0,0 +1,127 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.serial.test;
+
+import java.io.Serializable;
+
+import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * @author Mark Hayes
+ */
+@SuppressWarnings("serial")
+public class MarshalledObject
+ implements Serializable, MarshalledTupleKeyEntity {
+
+ private String data;
+ private transient String primaryKey;
+ private String indexKey1;
+ private String indexKey2;
+
+ public MarshalledObject(String data, String primaryKey,
+ String indexKey1, String indexKey2) {
+ this.data = data;
+ this.primaryKey = primaryKey;
+ this.indexKey1 = indexKey1;
+ this.indexKey2 = indexKey2;
+ }
+
+ public boolean equals(Object o) {
+
+ try {
+ MarshalledObject other = (MarshalledObject) o;
+
+ return this.data.equals(other.data) &&
+ this.primaryKey.equals(other.primaryKey) &&
+ this.indexKey1.equals(other.indexKey1) &&
+ this.indexKey2.equals(other.indexKey2);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public String getData() {
+
+ return data;
+ }
+
+ public String getPrimaryKey() {
+
+ return primaryKey;
+ }
+
+ public String getIndexKey1() {
+
+ return indexKey1;
+ }
+
+ public String getIndexKey2() {
+
+ return indexKey2;
+ }
+
+ public int expectedKeyLength() {
+
+ return primaryKey.length() + 1;
+ }
+
+ public void marshalPrimaryKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(primaryKey);
+ }
+
+ public void unmarshalPrimaryKey(TupleInput keyInput) {
+
+ primaryKey = keyInput.readString();
+ }
+
+ public boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput) {
+
+ if ("1".equals(keyName)) {
+ if (indexKey1.length() > 0) {
+ keyOutput.writeString(indexKey1);
+ return true;
+ } else {
+ return false;
+ }
+ } else if ("2".equals(keyName)) {
+ if (indexKey2.length() > 0) {
+ keyOutput.writeString(indexKey2);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown keyName: " + keyName);
+ }
+ }
+
+ public boolean nullifyForeignKey(String keyName) {
+
+ if ("1".equals(keyName)) {
+ if (indexKey1.length() > 0) {
+ indexKey1 = "";
+ return true;
+ } else {
+ return false;
+ }
+ } else if ("2".equals(keyName)) {
+ if (indexKey2.length() > 0) {
+ indexKey2 = "";
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown keyName: " + keyName);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/bind/serial/test/NullClassCatalog.java b/test/scr024/src/com/sleepycat/bind/serial/test/NullClassCatalog.java
new file mode 100644
index 00000000..a8ad23eb
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/serial/test/NullClassCatalog.java
@@ -0,0 +1,37 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.serial.test;
+
+import java.io.ObjectStreamClass;
+import java.math.BigInteger;
+
+import com.sleepycat.bind.serial.ClassCatalog;
+
+/**
+ * NullCatalog is a dummy Catalog implementation that simply
+ * returns large (8 byte) class IDs so that ObjectOutput
+ * can be simulated when computing a serialized size.
+ *
+ * @author Mark Hayes
+ */
+class NullClassCatalog implements ClassCatalog {
+
+ private long id = Long.MAX_VALUE;
+
+ public void close() {
+ }
+
+ public byte[] getClassID(ObjectStreamClass classFormat) {
+ return BigInteger.valueOf(id--).toByteArray();
+ }
+
+ public ObjectStreamClass getClassFormat(byte[] classID) {
+ return null; // ObjectInput not supported
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/bind/serial/test/SerialBindingTest.java b/test/scr024/src/com/sleepycat/bind/serial/test/SerialBindingTest.java
new file mode 100644
index 00000000..a9ddbed9
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/serial/test/SerialBindingTest.java
@@ -0,0 +1,330 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.serial.test;
+
+import java.io.Serializable;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.serial.SerialSerialBinding;
+import com.sleepycat.bind.serial.TupleSerialMarshalledBinding;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.FastOutputStream;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class SerialBindingTest extends TestCase {
+
+ private ClassCatalog catalog;
+ private DatabaseEntry buffer;
+ private DatabaseEntry keyBuffer;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(SerialBindingTest.class);
+ return suite;
+ }
+
+ public SerialBindingTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+
+ SharedTestUtils.printTestName("SerialBindingTest." + getName());
+ catalog = new TestClassCatalog();
+ buffer = new DatabaseEntry();
+ keyBuffer = new DatabaseEntry();
+ }
+
+ @Override
+ public void tearDown() {
+
+ /* Ensure that GC can cleanup. */
+ catalog = null;
+ buffer = null;
+ keyBuffer = null;
+ }
+
+ @Override
+ public void runTest()
+ throws Throwable {
+
+ try {
+ super.runTest();
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ }
+ }
+
+ private void primitiveBindingTest(Object val) {
+
+ Class cls = val.getClass();
+ SerialBinding binding = new SerialBinding(catalog, cls);
+
+ binding.objectToEntry(val, buffer);
+ assertTrue(buffer.getSize() > 0);
+
+ Object val2 = binding.entryToObject(buffer);
+ assertSame(cls, val2.getClass());
+ assertEquals(val, val2);
+
+ Object valWithWrongCls = (cls == String.class)
+ ? ((Object) new Integer(0)) : ((Object) new String(""));
+ try {
+ binding.objectToEntry(valWithWrongCls, buffer);
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ public void testPrimitiveBindings() {
+
+ primitiveBindingTest("abc");
+ primitiveBindingTest(new Character('a'));
+ primitiveBindingTest(new Boolean(true));
+ primitiveBindingTest(new Byte((byte) 123));
+ primitiveBindingTest(new Short((short) 123));
+ primitiveBindingTest(new Integer(123));
+ primitiveBindingTest(new Long(123));
+ primitiveBindingTest(new Float(123.123));
+ primitiveBindingTest(new Double(123.123));
+ }
+
+ public void testNullObjects() {
+
+ SerialBinding binding = new SerialBinding(catalog, null);
+ buffer.setSize(0);
+ binding.objectToEntry(null, buffer);
+ assertTrue(buffer.getSize() > 0);
+ assertEquals(null, binding.entryToObject(buffer));
+ }
+
+ public void testSerialSerialBinding() {
+
+ SerialBinding keyBinding = new SerialBinding(catalog, String.class);
+ SerialBinding valueBinding = new SerialBinding(catalog, String.class);
+ EntityBinding binding = new MySerialSerialBinding(keyBinding,
+ valueBinding);
+
+ String val = "key#value?indexKey";
+ binding.objectToData(val, buffer);
+ assertTrue(buffer.getSize() > 0);
+ binding.objectToKey(val, keyBuffer);
+ assertTrue(keyBuffer.getSize() > 0);
+
+ Object result = binding.entryToObject(keyBuffer, buffer);
+ assertEquals(val, result);
+ }
+
+ // also tests TupleSerialBinding since TupleSerialMarshalledBinding extends
+ // it
+ public void testTupleSerialMarshalledBinding() {
+
+ SerialBinding valueBinding = new SerialBinding(catalog,
+ MarshalledObject.class);
+ EntityBinding binding =
+ new TupleSerialMarshalledBinding(valueBinding);
+
+ MarshalledObject val = new MarshalledObject("abc", "primary",
+ "index1", "index2");
+ binding.objectToData(val, buffer);
+ assertTrue(buffer.getSize() > 0);
+ binding.objectToKey(val, keyBuffer);
+ assertEquals(val.expectedKeyLength(), keyBuffer.getSize());
+
+ Object result = binding.entryToObject(keyBuffer, buffer);
+ assertTrue(result instanceof MarshalledObject);
+ val = (MarshalledObject) result;
+ assertEquals("abc", val.getData());
+ assertEquals("primary", val.getPrimaryKey());
+ assertEquals("index1", val.getIndexKey1());
+ assertEquals("index2", val.getIndexKey2());
+ }
+
+ public void testBufferSize() {
+
+ CaptureSizeBinding binding =
+ new CaptureSizeBinding(catalog, String.class);
+
+ binding.objectToEntry("x", buffer);
+ assertEquals("x", binding.entryToObject(buffer));
+ assertEquals(FastOutputStream.DEFAULT_INIT_SIZE, binding.bufSize);
+
+ binding.setSerialBufferSize(1000);
+ binding.objectToEntry("x", buffer);
+ assertEquals("x", binding.entryToObject(buffer));
+ assertEquals(1000, binding.bufSize);
+ }
+
+ private static class CaptureSizeBinding extends SerialBinding {
+
+ int bufSize;
+
+ CaptureSizeBinding(ClassCatalog classCatalog, Class baseClass) {
+ super(classCatalog, baseClass);
+ }
+
+ @Override
+ public FastOutputStream getSerialOutput(Object object) {
+ FastOutputStream fos = super.getSerialOutput(object);
+ bufSize = fos.getBufferBytes().length;
+ return fos;
+ }
+ }
+
+ public void testBufferOverride() {
+
+ FastOutputStream out = new FastOutputStream(10);
+ CachedOutputBinding binding =
+ new CachedOutputBinding(catalog, String.class, out);
+
+ binding.used = false;
+ binding.objectToEntry("x", buffer);
+ assertEquals("x", binding.entryToObject(buffer));
+ assertTrue(binding.used);
+
+ binding.used = false;
+ binding.objectToEntry("aaaaaaaaaaaaaaaaaaaaaa", buffer);
+ assertEquals("aaaaaaaaaaaaaaaaaaaaaa", binding.entryToObject(buffer));
+ assertTrue(binding.used);
+
+ binding.used = false;
+ binding.objectToEntry("x", buffer);
+ assertEquals("x", binding.entryToObject(buffer));
+ assertTrue(binding.used);
+ }
+
+ private static class CachedOutputBinding extends SerialBinding {
+
+ FastOutputStream out;
+ boolean used;
+
+ CachedOutputBinding(ClassCatalog classCatalog,
+ Class baseClass,
+ FastOutputStream out) {
+ super(classCatalog, baseClass);
+ this.out = out;
+ }
+
+ @Override
+ public FastOutputStream getSerialOutput(Object object) {
+ out.reset();
+ used = true;
+ return out;
+ }
+ }
+
+ private static class MySerialSerialBinding extends SerialSerialBinding {
+
+ private MySerialSerialBinding(SerialBinding keyBinding,
+ SerialBinding valueBinding) {
+
+ super(keyBinding, valueBinding);
+ }
+
+ @Override
+ public Object entryToObject(Object keyInput, Object valueInput) {
+
+ return "" + keyInput + '#' + valueInput;
+ }
+
+ @Override
+ public Object objectToKey(Object object) {
+
+ String s = (String) object;
+ int i = s.indexOf('#');
+ if (i < 0 || i == s.length() - 1) {
+ throw new IllegalArgumentException(s);
+ } else {
+ return s.substring(0, i);
+ }
+ }
+
+ @Override
+ public Object objectToData(Object object) {
+
+ String s = (String) object;
+ int i = s.indexOf('#');
+ if (i < 0 || i == s.length() - 1) {
+ throw new IllegalArgumentException(s);
+ } else {
+ return s.substring(i + 1);
+ }
+ }
+ }
+
+ /**
+ * Tests that overriding SerialBinding.getClassLoader is possible. This is
+ * a crude test because to create a truly working class loader is a large
+ * undertaking.
+ */
+ public void testClassloaderOverride() {
+ DatabaseEntry entry = new DatabaseEntry();
+
+ SerialBinding binding = new CustomLoaderBinding
+ (catalog, null, new FailureClassLoader());
+
+ try {
+ binding.objectToEntry(new MyClass(), entry);
+ binding.entryToObject(entry);
+ fail();
+ } catch (RuntimeException e) {
+ assertTrue(e.getMessage().startsWith("expect failure"));
+ }
+ }
+
+ private static class CustomLoaderBinding extends SerialBinding {
+
+ private final ClassLoader loader;
+
+ CustomLoaderBinding(ClassCatalog classCatalog,
+ Class baseClass,
+ ClassLoader loader) {
+
+ super(classCatalog, baseClass);
+ this.loader = loader;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return loader;
+ }
+ }
+
+ private static class FailureClassLoader extends ClassLoader {
+
+ @Override
+ public Class loadClass(String name) {
+ throw new RuntimeException("expect failure: " + name);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ private static class MyClass implements Serializable {
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/bind/serial/test/TestClassCatalog.java b/test/scr024/src/com/sleepycat/bind/serial/test/TestClassCatalog.java
new file mode 100644
index 00000000..53113026
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/serial/test/TestClassCatalog.java
@@ -0,0 +1,56 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.serial.test;
+
+import java.io.ObjectStreamClass;
+import java.util.HashMap;
+
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * @author Mark Hayes
+ */
+public class TestClassCatalog implements ClassCatalog {
+
+ private final HashMap idToDescMap = new HashMap();
+ private final HashMap nameToIdMap = new HashMap();
+ private int nextId = 1;
+
+ public TestClassCatalog() {
+ }
+
+ public void close() {
+ }
+
+ public synchronized byte[] getClassID(ObjectStreamClass desc) {
+ String className = desc.getName();
+ byte[] id = (byte[]) nameToIdMap.get(className);
+ if (id == null) {
+ String strId = String.valueOf(nextId);
+ id = strId.getBytes();
+ nextId += 1;
+
+ idToDescMap.put(strId, desc);
+ nameToIdMap.put(className, id);
+ }
+ return id;
+ }
+
+ public synchronized ObjectStreamClass getClassFormat(byte[] id)
+ throws DatabaseException {
+
+ String strId = new String(id);
+ ObjectStreamClass desc = (ObjectStreamClass) idToDescMap.get(strId);
+ if (desc == null) {
+ throw new DatabaseException("classID not found");
+ }
+ return desc;
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/bind/test/BindingSpeedTest.java b/test/scr024/src/com/sleepycat/bind/test/BindingSpeedTest.java
new file mode 100644
index 00000000..8bf52d59
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/test/BindingSpeedTest.java
@@ -0,0 +1,484 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.test;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Serializable;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import com.sleepycat.bind.serial.SerialInput;
+import com.sleepycat.bind.serial.SerialOutput;
+import com.sleepycat.bind.serial.test.TestClassCatalog;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.util.FastInputStream;
+import com.sleepycat.util.FastOutputStream;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class BindingSpeedTest extends TestCase {
+
+ static final String JAVA_UNSHARED = "java-unshared".intern();
+ static final String JAVA_SHARED = "java-shared".intern();
+ static final String JAVA_EXTERNALIZABLE = "java-externalizable".intern();
+ static final String XML_SAX = "xml-sax".intern();
+ static final String TUPLE = "tuple".intern();
+ static final String REFLECT_METHOD = "reflectMethod".intern();
+ static final String REFLECT_FIELD = "reflectField".intern();
+
+ static final int RUN_COUNT = 1000;
+ static final boolean VERBOSE = false;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+
+ TestSuite suite = new TestSuite();
+ suite.addTest(new BindingSpeedTest(JAVA_UNSHARED));
+ suite.addTest(new BindingSpeedTest(JAVA_SHARED));
+ suite.addTest(new BindingSpeedTest(JAVA_EXTERNALIZABLE));
+ suite.addTest(new BindingSpeedTest(XML_SAX));
+ suite.addTest(new BindingSpeedTest(TUPLE));
+ suite.addTest(new BindingSpeedTest(REFLECT_METHOD));
+ suite.addTest(new BindingSpeedTest(REFLECT_FIELD));
+ return suite;
+ }
+
+ private String command;
+ private FastOutputStream fo;
+ private TupleOutput to;
+ private TestClassCatalog jtc;
+ private byte[] buf;
+ private XMLReader parser;
+ private Method[] getters;
+ private Method[] setters;
+ private Field[] fields;
+
+ public BindingSpeedTest(String name) {
+
+ super("BindingSpeedTest." + name);
+ command = name;
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+
+ boolean isTuple = false;
+ boolean isReflectMethod = false;
+ boolean isReflectField = false;
+ boolean isXmlSax = false;
+ boolean isSerial = false;
+ boolean isShared = false;
+ boolean isExternalizable = false;
+
+ if (command == TUPLE) {
+ isTuple = true;
+ } else if (command == REFLECT_METHOD) {
+ isReflectMethod = true;
+ } else if (command == REFLECT_FIELD) {
+ isReflectField = true;
+ } else if (command == XML_SAX) {
+ isXmlSax = true;
+ } else if (command == JAVA_UNSHARED) {
+ isSerial = true;
+ } else if (command == JAVA_SHARED) {
+ isSerial = true;
+ isShared = true;
+ } else if (command == JAVA_EXTERNALIZABLE) {
+ isSerial = true;
+ isShared = true;
+ isExternalizable = true;
+ } else {
+ throw new Exception("invalid command: " + command);
+ }
+
+ // Do initialization
+
+ if (isTuple) {
+ initTuple();
+ } else if (isReflectMethod) {
+ initReflectMethod();
+ } else if (isReflectField) {
+ initReflectField();
+ } else if (isXmlSax) {
+ initXmlSax();
+ } else if (isSerial) {
+ if (isShared) {
+ initSerialShared();
+ } else {
+ initSerialUnshared();
+ }
+ }
+
+ // Prime the Java compiler
+
+ int size = 0;
+ for (int i = 0; i < RUN_COUNT; i += 1) {
+
+ if (isTuple) {
+ size = runTuple();
+ } else if (isReflectMethod) {
+ size = runReflectMethod();
+ } else if (isReflectField) {
+ size = runReflectField();
+ } else if (isXmlSax) {
+ size = runXmlSax();
+ } else if (isSerial) {
+ if (isShared) {
+ if (isExternalizable) {
+ size = runSerialExternalizable();
+ } else {
+ size = runSerialShared();
+ }
+ } else {
+ size = runSerialUnshared();
+ }
+ }
+ }
+
+ // Then run the timing tests
+
+ long startTime = System.currentTimeMillis();
+
+ for (int i = 0; i < RUN_COUNT; i += 1) {
+ if (isTuple) {
+ size = runTuple();
+ } else if (isReflectMethod) {
+ size = runReflectMethod();
+ } else if (isReflectField) {
+ size = runReflectField();
+ } else if (isXmlSax) {
+ size = runXmlSax();
+ } else if (isSerial) {
+ if (isShared) {
+ if (isExternalizable) {
+ size = runSerialExternalizable();
+ } else {
+ size = runSerialShared();
+ }
+ } else {
+ size = runSerialUnshared();
+ }
+ }
+ }
+
+ long stopTime = System.currentTimeMillis();
+
+ assertTrue("data size too big", size < 250);
+
+ if (VERBOSE) {
+ System.out.println(command);
+ System.out.println("data size: " + size);
+ System.out.println("run time: " +
+ ((stopTime - startTime) / (double) RUN_COUNT));
+ }
+ }
+
+ @Override
+ public void tearDown() {
+
+ /* Ensure that GC can cleanup. */
+ command = null;
+ fo = null;
+ to = null;
+ jtc = null;
+ buf = null;
+ parser = null;
+ }
+
+ void initSerialUnshared() {
+ fo = new FastOutputStream();
+ }
+
+ int runSerialUnshared()
+ throws Exception {
+
+ fo.reset();
+ ObjectOutputStream oos = new ObjectOutputStream(fo);
+ oos.writeObject(new Data());
+ byte[] bytes = fo.toByteArray();
+ FastInputStream fi = new FastInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(fi);
+ ois.readObject();
+ return bytes.length;
+ }
+
+ void initSerialShared() {
+ jtc = new TestClassCatalog();
+ fo = new FastOutputStream();
+ }
+
+ int runSerialShared()
+ throws Exception {
+
+ fo.reset();
+ SerialOutput oos = new SerialOutput(fo, jtc);
+ oos.writeObject(new Data());
+ byte[] bytes = fo.toByteArray();
+ FastInputStream fi = new FastInputStream(bytes);
+ SerialInput ois = new SerialInput(fi, jtc);
+ ois.readObject();
+ return (bytes.length - SerialOutput.getStreamHeader().length);
+ }
+
+ int runSerialExternalizable()
+ throws Exception {
+
+ fo.reset();
+ SerialOutput oos = new SerialOutput(fo, jtc);
+ oos.writeObject(new Data2());
+ byte[] bytes = fo.toByteArray();
+ FastInputStream fi = new FastInputStream(bytes);
+ SerialInput ois = new SerialInput(fi, jtc);
+ ois.readObject();
+ return (bytes.length - SerialOutput.getStreamHeader().length);
+ }
+
+ void initTuple() {
+ buf = new byte[500];
+ to = new TupleOutput(buf);
+ }
+
+ int runTuple() {
+ to.reset();
+ new Data().writeTuple(to);
+
+ TupleInput ti = new TupleInput(
+ to.getBufferBytes(), to.getBufferOffset(),
+ to.getBufferLength());
+ new Data().readTuple(ti);
+
+ return to.getBufferLength();
+ }
+
+ void initReflectMethod()
+ throws Exception {
+
+ initTuple();
+
+ Class cls = Data.class;
+
+ getters = new Method[5];
+ getters[0] = cls.getMethod("getField1", new Class[0]);
+ getters[1] = cls.getMethod("getField2", new Class[0]);
+ getters[2] = cls.getMethod("getField3", new Class[0]);
+ getters[3] = cls.getMethod("getField4", new Class[0]);
+ getters[4] = cls.getMethod("getField5", new Class[0]);
+
+ setters = new Method[5];
+ setters[0] = cls.getMethod("setField1", new Class[] {String.class});
+ setters[1] = cls.getMethod("setField2", new Class[] {String.class});
+ setters[2] = cls.getMethod("setField3", new Class[] {Integer.TYPE});
+ setters[3] = cls.getMethod("setField4", new Class[] {Integer.TYPE});
+ setters[4] = cls.getMethod("setField5", new Class[] {String.class});
+ }
+
+ int runReflectMethod()
+ throws Exception {
+
+ to.reset();
+ Data data = new Data();
+ to.writeString((String) getters[0].invoke(data, (Object[]) null));
+ to.writeString((String) getters[1].invoke(data, (Object[]) null));
+ to.writeInt(((Integer) getters[2].invoke(data, (Object[]) null)).intValue());
+ to.writeInt(((Integer) getters[3].invoke(data, (Object[]) null)).intValue());
+ to.writeString((String) getters[4].invoke(data, (Object[]) null));
+
+ TupleInput ti = new TupleInput(
+ to.getBufferBytes(), to.getBufferOffset(),
+ to.getBufferLength());
+ data = new Data();
+ setters[0].invoke(data, new Object[] {ti.readString()});
+ setters[1].invoke(data, new Object[] {ti.readString()});
+ setters[2].invoke(data, new Object[] {new Integer(ti.readInt())});
+ setters[3].invoke(data, new Object[] {new Integer(ti.readInt())});
+ setters[4].invoke(data, new Object[] {ti.readString()});
+
+ return to.getBufferLength();
+ }
+
+ void initReflectField()
+ throws Exception {
+
+ initTuple();
+
+ Class cls = Data.class;
+
+ fields = new Field[5];
+ fields[0] = cls.getField("field1");
+ fields[1] = cls.getField("field2");
+ fields[2] = cls.getField("field3");
+ fields[3] = cls.getField("field4");
+ fields[4] = cls.getField("field5");
+ }
+
+ int runReflectField()
+ throws Exception {
+
+ to.reset();
+ Data data = new Data();
+ to.writeString((String) fields[0].get(data));
+ to.writeString((String) fields[1].get(data));
+ to.writeInt(((Integer) fields[2].get(data)).intValue());
+ to.writeInt(((Integer) fields[3].get(data)).intValue());
+ to.writeString((String) fields[4].get(data));
+
+ TupleInput ti = new TupleInput(
+ to.getBufferBytes(), to.getBufferOffset(),
+ to.getBufferLength());
+ data = new Data();
+ fields[0].set(data, ti.readString());
+ fields[1].set(data, ti.readString());
+ fields[2].set(data, new Integer(ti.readInt()));
+ fields[3].set(data, new Integer(ti.readInt()));
+ fields[4].set(data, ti.readString());
+
+ return to.getBufferLength();
+ }
+
+ void initXmlSax()
+ throws Exception {
+
+ buf = new byte[500];
+ fo = new FastOutputStream();
+ SAXParserFactory saxFactory = SAXParserFactory.newInstance();
+ saxFactory.setNamespaceAware(true);
+ parser = saxFactory.newSAXParser().getXMLReader();
+ }
+
+ int runXmlSax()
+ throws Exception {
+
+ fo.reset();
+ OutputStreamWriter writer = new OutputStreamWriter(fo);
+ new Data().writeXmlText(writer);
+
+ byte[] bytes = fo.toByteArray();
+ FastInputStream fi = new FastInputStream(bytes);
+ InputSource input = new InputSource(fi);
+ parser.parse(input);
+
+ //InputStreamReader reader = new InputStreamReader(fi);
+ //new Data().readXmlText(??);
+
+ return bytes.length;
+ }
+
+ static class Data2 extends Data implements Externalizable {
+
+ public Data2() {}
+
+ public void readExternal(ObjectInput in)
+ throws IOException {
+
+ field1 = in.readUTF();
+ field2 = in.readUTF();
+ field3 = in.readInt();
+ field4 = in.readInt();
+ field5 = in.readUTF();
+ }
+
+ public void writeExternal(ObjectOutput out)
+ throws IOException {
+
+ out.writeUTF(field1);
+ out.writeUTF(field2);
+ out.writeInt(field3);
+ out.writeInt(field4);
+ out.writeUTF(field5);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static class Data implements Serializable {
+
+ public String field1 = "field1";
+ public String field2 = "field2";
+ public int field3 = 333;
+ public int field4 = 444;
+ public String field5 = "field5";
+
+ public String getField1() { return field1; }
+ public String getField2() { return field2; }
+ public int getField3() { return field3; }
+ public int getField4() { return field4; }
+ public String getField5() { return field5; }
+
+ public void setField1(String v) { field1 = v; }
+ public void setField2(String v) { field2 = v; }
+ public void setField3(int v) { field3 = v; }
+ public void setField4(int v) { field4 = v; }
+ public void setField5(String v) { field5 = v; }
+
+ void readTuple(TupleInput _input) {
+
+ field1 = _input.readString();
+ field2 = _input.readString();
+ field3 = _input.readInt();
+ field4 = _input.readInt();
+ field5 = _input.readString();
+ }
+
+ void writeTuple(TupleOutput _output) {
+
+ _output.writeString(field1);
+ _output.writeString(field2);
+ _output.writeInt(field3);
+ _output.writeInt(field4);
+ _output.writeString(field5);
+ }
+
+ void writeXmlText(Writer writer) throws IOException {
+
+ writer.write("<Data><Field1>");
+ writer.write(field1);
+ writer.write("</Field1><Field2>");
+ writer.write(field2);
+ writer.write("</Field2><Field3>");
+ writer.write(String.valueOf(field3));
+ writer.write("</Field3><Field4>");
+ writer.write(String.valueOf(field4));
+ writer.write("</Field4><Field5>");
+ writer.write(field5);
+ writer.write("</Field5></Data>");
+ writer.flush();
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/bind/tuple/test/MarshalledObject.java b/test/scr024/src/com/sleepycat/bind/tuple/test/MarshalledObject.java
new file mode 100644
index 00000000..bf98785a
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/tuple/test/MarshalledObject.java
@@ -0,0 +1,136 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.tuple.test;
+
+import com.sleepycat.bind.tuple.MarshalledTupleEntry;
+import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * @author Mark Hayes
+ */
+public class MarshalledObject
+ implements MarshalledTupleEntry, MarshalledTupleKeyEntity {
+
+ private String data;
+ private String primaryKey;
+ private String indexKey1;
+ private String indexKey2;
+
+ public MarshalledObject() {
+ }
+
+ MarshalledObject(String data, String primaryKey,
+ String indexKey1, String indexKey2) {
+
+ this.data = data;
+ this.primaryKey = primaryKey;
+ this.indexKey1 = indexKey1;
+ this.indexKey2 = indexKey2;
+ }
+
+ String getData() {
+
+ return data;
+ }
+
+ String getPrimaryKey() {
+
+ return primaryKey;
+ }
+
+ String getIndexKey1() {
+
+ return indexKey1;
+ }
+
+ String getIndexKey2() {
+
+ return indexKey2;
+ }
+
+ int expectedDataLength() {
+
+ return data.length() + 1 +
+ indexKey1.length() + 1 +
+ indexKey2.length() + 1;
+ }
+
+ int expectedKeyLength() {
+
+ return primaryKey.length() + 1;
+ }
+
+ public void marshalEntry(TupleOutput dataOutput) {
+
+ dataOutput.writeString(data);
+ dataOutput.writeString(indexKey1);
+ dataOutput.writeString(indexKey2);
+ }
+
+ public void unmarshalEntry(TupleInput dataInput) {
+
+ data = dataInput.readString();
+ indexKey1 = dataInput.readString();
+ indexKey2 = dataInput.readString();
+ }
+
+ public void marshalPrimaryKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(primaryKey);
+ }
+
+ public void unmarshalPrimaryKey(TupleInput keyInput) {
+
+ primaryKey = keyInput.readString();
+ }
+
+ public boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput) {
+
+ if ("1".equals(keyName)) {
+ if (indexKey1.length() > 0) {
+ keyOutput.writeString(indexKey1);
+ return true;
+ } else {
+ return false;
+ }
+ } else if ("2".equals(keyName)) {
+ if (indexKey1.length() > 0) {
+ keyOutput.writeString(indexKey2);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown keyName: " + keyName);
+ }
+ }
+
+ public boolean nullifyForeignKey(String keyName) {
+
+ if ("1".equals(keyName)) {
+ if (indexKey1.length() > 0) {
+ indexKey1 = "";
+ return true;
+ } else {
+ return false;
+ }
+ } else if ("2".equals(keyName)) {
+ if (indexKey1.length() > 0) {
+ indexKey2 = "";
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown keyName: " + keyName);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/bind/tuple/test/TupleBindingTest.java b/test/scr024/src/com/sleepycat/bind/tuple/test/TupleBindingTest.java
new file mode 100644
index 00000000..8f315ff6
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/tuple/test/TupleBindingTest.java
@@ -0,0 +1,426 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.tuple.test;
+
+import java.math.BigInteger;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.tuple.BigIntegerBinding;
+import com.sleepycat.bind.tuple.BooleanBinding;
+import com.sleepycat.bind.tuple.ByteBinding;
+import com.sleepycat.bind.tuple.CharacterBinding;
+import com.sleepycat.bind.tuple.DoubleBinding;
+import com.sleepycat.bind.tuple.FloatBinding;
+import com.sleepycat.bind.tuple.IntegerBinding;
+import com.sleepycat.bind.tuple.LongBinding;
+import com.sleepycat.bind.tuple.ShortBinding;
+import com.sleepycat.bind.tuple.SortedDoubleBinding;
+import com.sleepycat.bind.tuple.SortedFloatBinding;
+import com.sleepycat.bind.tuple.StringBinding;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleInputBinding;
+import com.sleepycat.bind.tuple.TupleMarshalledBinding;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.bind.tuple.TupleTupleMarshalledBinding;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.FastOutputStream;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class TupleBindingTest extends TestCase {
+
+ private DatabaseEntry buffer;
+ private DatabaseEntry keyBuffer;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(TupleBindingTest.class);
+ return suite;
+ }
+
+ public TupleBindingTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+
+ SharedTestUtils.printTestName("TupleBindingTest." + getName());
+ buffer = new DatabaseEntry();
+ keyBuffer = new DatabaseEntry();
+ }
+
+ @Override
+ public void tearDown() {
+
+ /* Ensure that GC can cleanup. */
+ buffer = null;
+ keyBuffer = null;
+ }
+
+ @Override
+ public void runTest()
+ throws Throwable {
+
+ try {
+ super.runTest();
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ }
+ }
+
+ private void primitiveBindingTest(Class primitiveCls, Class compareCls,
+ Object val, int byteSize) {
+
+ TupleBinding binding = TupleBinding.getPrimitiveBinding(primitiveCls);
+
+ /* Test standard object binding. */
+
+ binding.objectToEntry(val, buffer);
+ assertEquals(byteSize, buffer.getSize());
+
+ Object val2 = binding.entryToObject(buffer);
+ assertSame(compareCls, val2.getClass());
+ assertEquals(val, val2);
+
+ Object valWithWrongCls = (primitiveCls == String.class)
+ ? ((Object) new Integer(0)) : ((Object) new String(""));
+ try {
+ binding.objectToEntry(valWithWrongCls, buffer);
+ }
+ catch (ClassCastException expected) {}
+
+ /* Test nested tuple binding. */
+ forMoreCoverageTest(binding, val);
+ }
+
+ private void forMoreCoverageTest(TupleBinding val1,Object val2) {
+
+ TupleOutput output = new TupleOutput();
+ output.writeString("abc");
+ val1.objectToEntry(val2, output);
+ output.writeString("xyz");
+
+ TupleInput input = new TupleInput(output);
+ assertEquals("abc", input.readString());
+ Object val3 = val1.entryToObject(input);
+ assertEquals("xyz", input.readString());
+
+ assertEquals(0, input.available());
+ assertSame(val2.getClass(), val3.getClass());
+ assertEquals(val2, val3);
+ }
+
+ public void testPrimitiveBindings() {
+
+ primitiveBindingTest(String.class, String.class,
+ "abc", 4);
+
+ primitiveBindingTest(Character.class, Character.class,
+ new Character('a'), 2);
+ primitiveBindingTest(Boolean.class, Boolean.class,
+ new Boolean(true), 1);
+ primitiveBindingTest(Byte.class, Byte.class,
+ new Byte((byte) 123), 1);
+ primitiveBindingTest(Short.class, Short.class,
+ new Short((short) 123), 2);
+ primitiveBindingTest(Integer.class, Integer.class,
+ new Integer(123), 4);
+ primitiveBindingTest(Long.class, Long.class,
+ new Long(123), 8);
+ primitiveBindingTest(Float.class, Float.class,
+ new Float(123.123), 4);
+ primitiveBindingTest(Double.class, Double.class,
+ new Double(123.123), 8);
+
+ primitiveBindingTest(Character.TYPE, Character.class,
+ new Character('a'), 2);
+ primitiveBindingTest(Boolean.TYPE, Boolean.class,
+ new Boolean(true), 1);
+ primitiveBindingTest(Byte.TYPE, Byte.class,
+ new Byte((byte) 123), 1);
+ primitiveBindingTest(Short.TYPE, Short.class,
+ new Short((short) 123), 2);
+ primitiveBindingTest(Integer.TYPE, Integer.class,
+ new Integer(123), 4);
+ primitiveBindingTest(Long.TYPE, Long.class,
+ new Long(123), 8);
+ primitiveBindingTest(Float.TYPE, Float.class,
+ new Float(123.123), 4);
+ primitiveBindingTest(Double.TYPE, Double.class,
+ new Double(123.123), 8);
+
+ DatabaseEntry entry = new DatabaseEntry();
+
+ StringBinding.stringToEntry("abc", entry);
+ assertEquals(4, entry.getData().length);
+ assertEquals("abc", StringBinding.entryToString(entry));
+
+ new StringBinding().objectToEntry("abc", entry);
+ assertEquals(4, entry.getData().length);
+
+ StringBinding.stringToEntry(null, entry);
+ assertEquals(2, entry.getData().length);
+ assertEquals(null, StringBinding.entryToString(entry));
+
+ new StringBinding().objectToEntry(null, entry);
+ assertEquals(2, entry.getData().length);
+
+ CharacterBinding.charToEntry('a', entry);
+ assertEquals(2, entry.getData().length);
+ assertEquals('a', CharacterBinding.entryToChar(entry));
+
+ new CharacterBinding().objectToEntry(new Character('a'), entry);
+ assertEquals(2, entry.getData().length);
+
+ BooleanBinding.booleanToEntry(true, entry);
+ assertEquals(1, entry.getData().length);
+ assertEquals(true, BooleanBinding.entryToBoolean(entry));
+
+ new BooleanBinding().objectToEntry(Boolean.TRUE, entry);
+ assertEquals(1, entry.getData().length);
+
+ ByteBinding.byteToEntry((byte) 123, entry);
+ assertEquals(1, entry.getData().length);
+ assertEquals((byte) 123, ByteBinding.entryToByte(entry));
+
+ ShortBinding.shortToEntry((short) 123, entry);
+ assertEquals(2, entry.getData().length);
+ assertEquals((short) 123, ShortBinding.entryToShort(entry));
+
+ new ByteBinding().objectToEntry(new Byte((byte) 123), entry);
+ assertEquals(1, entry.getData().length);
+
+ IntegerBinding.intToEntry(123, entry);
+ assertEquals(4, entry.getData().length);
+ assertEquals(123, IntegerBinding.entryToInt(entry));
+
+ new IntegerBinding().objectToEntry(new Integer(123), entry);
+ assertEquals(4, entry.getData().length);
+
+ LongBinding.longToEntry(123, entry);
+ assertEquals(8, entry.getData().length);
+ assertEquals(123, LongBinding.entryToLong(entry));
+
+ new LongBinding().objectToEntry(new Long(123), entry);
+ assertEquals(8, entry.getData().length);
+
+ FloatBinding.floatToEntry((float) 123.123, entry);
+ assertEquals(4, entry.getData().length);
+ assertTrue(((float) 123.123) == FloatBinding.entryToFloat(entry));
+
+ new FloatBinding().objectToEntry(new Float((float) 123.123), entry);
+ assertEquals(4, entry.getData().length);
+
+ DoubleBinding.doubleToEntry(123.123, entry);
+ assertEquals(8, entry.getData().length);
+ assertTrue(123.123 == DoubleBinding.entryToDouble(entry));
+
+ new DoubleBinding().objectToEntry(new Double(123.123), entry);
+ assertEquals(8, entry.getData().length);
+
+ BigIntegerBinding.bigIntegerToEntry
+ (new BigInteger("1234567890123456"), entry);
+ assertEquals(9, entry.getData().length);
+ assertTrue((new BigInteger("1234567890123456")).equals
+ (BigIntegerBinding.entryToBigInteger(entry)));
+
+ new BigIntegerBinding().objectToEntry
+ (new BigInteger("1234567890123456"), entry);
+ assertEquals(9, entry.getData().length);
+ forMoreCoverageTest(new BigIntegerBinding(),
+ new BigInteger("1234567890123456"));
+
+ SortedFloatBinding.floatToEntry((float) 123.123, entry);
+ assertEquals(4, entry.getData().length);
+ assertTrue(((float) 123.123) ==
+ SortedFloatBinding.entryToFloat(entry));
+
+ new SortedFloatBinding().objectToEntry
+ (new Float((float) 123.123), entry);
+ assertEquals(4, entry.getData().length);
+ forMoreCoverageTest(new SortedFloatBinding(),
+ new Float((float) 123.123));
+
+ SortedDoubleBinding.doubleToEntry(123.123, entry);
+ assertEquals(8, entry.getData().length);
+ assertTrue(123.123 == SortedDoubleBinding.entryToDouble(entry));
+
+ new SortedDoubleBinding().objectToEntry(new Double(123.123), entry);
+ assertEquals(8, entry.getData().length);
+ forMoreCoverageTest(new SortedDoubleBinding(),
+ new Double(123.123));
+ }
+
+ public void testTupleInputBinding() {
+
+ EntryBinding binding = new TupleInputBinding();
+
+ TupleOutput out = new TupleOutput();
+ out.writeString("abc");
+ binding.objectToEntry(new TupleInput(out), buffer);
+ assertEquals(4, buffer.getSize());
+
+ Object result = binding.entryToObject(buffer);
+ assertTrue(result instanceof TupleInput);
+ TupleInput in = (TupleInput) result;
+ assertEquals("abc", in.readString());
+ assertEquals(0, in.available());
+ }
+
+ // also tests TupleBinding since TupleMarshalledBinding extends it
+ public void testTupleMarshalledBinding() {
+
+ EntryBinding binding =
+ new TupleMarshalledBinding(MarshalledObject.class);
+
+ MarshalledObject val = new MarshalledObject("abc", "", "", "");
+ binding.objectToEntry(val, buffer);
+ assertEquals(val.expectedDataLength(), buffer.getSize());
+
+ Object result = binding.entryToObject(buffer);
+ assertTrue(result instanceof MarshalledObject);
+ val = (MarshalledObject) result;
+ assertEquals("abc", val.getData());
+ }
+
+ // also tests TupleTupleBinding since TupleTupleMarshalledBinding extends
+ // it
+ public void testTupleTupleMarshalledBinding() {
+
+ EntityBinding binding =
+ new TupleTupleMarshalledBinding(MarshalledObject.class);
+
+ MarshalledObject val = new MarshalledObject("abc", "primary",
+ "index1", "index2");
+ binding.objectToData(val, buffer);
+ assertEquals(val.expectedDataLength(), buffer.getSize());
+ binding.objectToKey(val, keyBuffer);
+ assertEquals(val.expectedKeyLength(), keyBuffer.getSize());
+
+ Object result = binding.entryToObject(keyBuffer, buffer);
+ assertTrue(result instanceof MarshalledObject);
+ val = (MarshalledObject) result;
+ assertEquals("abc", val.getData());
+ assertEquals("primary", val.getPrimaryKey());
+ assertEquals("index1", val.getIndexKey1());
+ assertEquals("index2", val.getIndexKey2());
+ }
+
+ public void testBufferSize() {
+
+ CaptureSizeBinding binding = new CaptureSizeBinding();
+
+ binding.objectToEntry("x", buffer);
+ assertEquals("x", binding.entryToObject(buffer));
+ assertEquals(FastOutputStream.DEFAULT_INIT_SIZE, binding.bufSize);
+
+ binding.setTupleBufferSize(1000);
+ binding.objectToEntry("x", buffer);
+ assertEquals("x", binding.entryToObject(buffer));
+ assertEquals(1000, binding.bufSize);
+ }
+
+ private class CaptureSizeBinding extends TupleBinding {
+
+ int bufSize;
+
+ CaptureSizeBinding() {
+ super();
+ }
+
+ @Override
+ public TupleOutput getTupleOutput(Object object) {
+ TupleOutput out = super.getTupleOutput(object);
+ bufSize = out.getBufferBytes().length;
+ return out;
+ }
+
+ @Override
+ public Object entryToObject(TupleInput input) {
+ return input.readString();
+ }
+
+ @Override
+ public void objectToEntry(Object object, TupleOutput output) {
+ assertEquals(bufSize, output.getBufferBytes().length);
+ output.writeString((String) object);
+ }
+ }
+
+ public void testBufferOverride() {
+
+ TupleOutput out = new TupleOutput(new byte[10]);
+ CachedOutputBinding binding = new CachedOutputBinding(out);
+
+ binding.used = false;
+ binding.objectToEntry("x", buffer);
+ assertEquals("x", binding.entryToObject(buffer));
+ assertTrue(binding.used);
+
+ binding.used = false;
+ binding.objectToEntry("aaaaaaaaaaaaaaaaaaaaaa", buffer);
+ assertEquals("aaaaaaaaaaaaaaaaaaaaaa", binding.entryToObject(buffer));
+ assertTrue(binding.used);
+
+ binding.used = false;
+ binding.objectToEntry("x", buffer);
+ assertEquals("x", binding.entryToObject(buffer));
+ assertTrue(binding.used);
+ }
+
+ private class CachedOutputBinding extends TupleBinding {
+
+ TupleOutput out;
+ boolean used;
+
+ CachedOutputBinding(TupleOutput out) {
+ super();
+ this.out = out;
+ }
+
+ @Override
+ public TupleOutput getTupleOutput(Object object) {
+ out.reset();
+ used = true;
+ return out;
+ }
+
+ @Override
+ public Object entryToObject(TupleInput input) {
+ return input.readString();
+ }
+
+ @Override
+ public void objectToEntry(Object object, TupleOutput output) {
+ assertSame(out, output);
+ output.writeString((String) object);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/bind/tuple/test/TupleFormatTest.java b/test/scr024/src/com/sleepycat/bind/tuple/test/TupleFormatTest.java
new file mode 100644
index 00000000..4c2ece69
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/tuple/test/TupleFormatTest.java
@@ -0,0 +1,927 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.tuple.test;
+
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+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.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class TupleFormatTest extends TestCase {
+
+ private TupleInput in;
+ private TupleOutput out;
+ private DatabaseEntry buffer;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(TupleFormatTest.class);
+ return suite;
+ }
+
+ public TupleFormatTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+
+ SharedTestUtils.printTestName("TupleFormatTest." + getName());
+ buffer = new DatabaseEntry();
+ out = new TupleOutput();
+ }
+
+ @Override
+ public void tearDown() {
+
+ /* Ensure that GC can cleanup. */
+ in = null;
+ out = null;
+ buffer = null;
+ }
+
+ private void copyOutputToInput() {
+
+ TupleBinding.outputToEntry(out, buffer);
+ assertEquals(out.size(), buffer.getSize());
+ in = TupleBinding.entryToInput(buffer);
+ assertEquals(in.available(), buffer.getSize());
+ assertEquals(in.getBufferLength(), buffer.getSize());
+ }
+
+ private void stringTest(String val) {
+
+ out.reset();
+ out.writeString(val);
+ assertEquals(val.length() + 1, out.size()); // assume 1-byte chars
+ copyOutputToInput();
+ assertEquals(val, in.readString());
+ assertEquals(0, in.available());
+ }
+
+ public void testString() {
+
+ stringTest("");
+ stringTest("a");
+ stringTest("abc");
+
+ out.reset();
+ out.writeString("abc");
+ out.writeString("defg");
+ assertEquals(9, out.size());
+ copyOutputToInput();
+ assertEquals("abc", in.readString());
+ assertEquals("defg", in.readString());
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeString("abc");
+ out.writeString("defg");
+ out.writeString("hijkl");
+ assertEquals(15, out.size());
+ copyOutputToInput();
+ assertEquals("abc", in.readString());
+ assertEquals("defg", in.readString());
+ assertEquals("hijkl", in.readString());
+ assertEquals(0, in.available());
+ }
+
+ private void fixedStringTest(char[] val) {
+
+ out.reset();
+ out.writeString(val);
+ assertEquals(val.length, out.size()); // assume 1 byte chars
+ copyOutputToInput();
+ char[] val2 = new char[val.length];
+ in.readString(val2);
+ assertTrue(Arrays.equals(val, val2));
+ assertEquals(0, in.available());
+ in.reset();
+ String val3 = in.readString(val.length);
+ assertTrue(Arrays.equals(val, val3.toCharArray()));
+ assertEquals(0, in.available());
+ }
+
+ public void testFixedString() {
+
+ fixedStringTest(new char[0]);
+ fixedStringTest(new char[] {'a'});
+ fixedStringTest(new char[] {'a', 'b', 'c'});
+
+ out.reset();
+ out.writeString(new char[] {'a', 'b', 'c'});
+ out.writeString(new char[] {'d', 'e', 'f', 'g'});
+ assertEquals(7, out.size());
+ copyOutputToInput();
+ assertEquals("abc", in.readString(3));
+ assertEquals("defg", in.readString(4));
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeString(new char[] {'a', 'b', 'c'});
+ out.writeString(new char[] {'d', 'e', 'f', 'g'});
+ out.writeString(new char[] {'h', 'i', 'j', 'k', 'l'});
+ assertEquals(12, out.size());
+ copyOutputToInput();
+ assertEquals("abc", in.readString(3));
+ assertEquals("defg", in.readString(4));
+ assertEquals("hijkl", in.readString(5));
+ assertEquals(0, in.available());
+ }
+
+ public void testNullString() {
+
+ out.reset();
+ out.writeString((String) null);
+ assertEquals(2, out.size());
+ copyOutputToInput();
+ assertEquals(null, in.readString());
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeString((String) null);
+ out.writeString("x");
+ assertEquals(4, out.size());
+ copyOutputToInput();
+ assertEquals(null, in.readString());
+ assertEquals(2, in.available());
+ assertEquals("x", in.readString());
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeString("x");
+ out.writeString((String) null);
+ assertEquals(4, out.size());
+ copyOutputToInput();
+ assertEquals("x", in.readString());
+ assertEquals(2, in.available());
+ assertEquals(null, in.readString());
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeString((String) null);
+ out.writeInt(123);
+ assertEquals(6, out.size());
+ copyOutputToInput();
+ assertEquals(null, in.readString());
+ assertEquals(4, in.available());
+ assertEquals(123, in.readInt());
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeInt(123);
+ out.writeString((String) null);
+ assertEquals(6, out.size());
+ copyOutputToInput();
+ assertEquals(123, in.readInt());
+ assertEquals(2, in.available());
+ assertEquals(null, in.readString());
+ assertEquals(0, in.available());
+ }
+
+ private void charsTest(char[] val) {
+
+ for (int mode = 0; mode < 2; mode += 1) {
+ out.reset();
+ switch (mode) {
+ case 0: out.writeChars(val); break;
+ case 1: out.writeChars(new String(val)); break;
+ default: throw new IllegalStateException();
+ }
+ assertEquals(val.length * 2, out.size());
+ copyOutputToInput();
+ char[] val2 = new char[val.length];
+ in.readChars(val2);
+ assertTrue(Arrays.equals(val, val2));
+ assertEquals(0, in.available());
+ in.reset();
+ String val3 = in.readChars(val.length);
+ assertTrue(Arrays.equals(val, val3.toCharArray()));
+ assertEquals(0, in.available());
+ }
+ }
+
+ public void testChars() {
+
+ charsTest(new char[0]);
+ charsTest(new char[] {'a'});
+ charsTest(new char[] {'a', 'b', 'c'});
+
+ out.reset();
+ out.writeChars("abc");
+ out.writeChars("defg");
+ assertEquals(7 * 2, out.size());
+ copyOutputToInput();
+ assertEquals("abc", in.readChars(3));
+ assertEquals("defg", in.readChars(4));
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeChars("abc");
+ out.writeChars("defg");
+ out.writeChars("hijkl");
+ assertEquals(12 * 2, out.size());
+ copyOutputToInput();
+ assertEquals("abc", in.readChars(3));
+ assertEquals("defg", in.readChars(4));
+ assertEquals("hijkl", in.readChars(5));
+ assertEquals(0, in.available());
+ }
+
+ private void bytesTest(char[] val) {
+
+ char[] valBytes = new char[val.length];
+ for (int i = 0; i < val.length; i += 1)
+ valBytes[i] = (char) (val[i] & 0xFF);
+
+ for (int mode = 0; mode < 2; mode += 1) {
+ out.reset();
+ switch (mode) {
+ case 0: out.writeBytes(val); break;
+ case 1: out.writeBytes(new String(val)); break;
+ default: throw new IllegalStateException();
+ }
+ assertEquals(val.length, out.size());
+ copyOutputToInput();
+ char[] val2 = new char[val.length];
+ in.readBytes(val2);
+ assertTrue(Arrays.equals(valBytes, val2));
+ assertEquals(0, in.available());
+ in.reset();
+ String val3 = in.readBytes(val.length);
+ assertTrue(Arrays.equals(valBytes, val3.toCharArray()));
+ assertEquals(0, in.available());
+ }
+ }
+
+ public void testBytes() {
+
+ bytesTest(new char[0]);
+ bytesTest(new char[] {'a'});
+ bytesTest(new char[] {'a', 'b', 'c'});
+ bytesTest(new char[] {0x7F00, 0x7FFF, 0xFF00, 0xFFFF});
+
+ out.reset();
+ out.writeBytes("abc");
+ out.writeBytes("defg");
+ assertEquals(7, out.size());
+ copyOutputToInput();
+ assertEquals("abc", in.readBytes(3));
+ assertEquals("defg", in.readBytes(4));
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeBytes("abc");
+ out.writeBytes("defg");
+ out.writeBytes("hijkl");
+ assertEquals(12, out.size());
+ copyOutputToInput();
+ assertEquals("abc", in.readBytes(3));
+ assertEquals("defg", in.readBytes(4));
+ assertEquals("hijkl", in.readBytes(5));
+ assertEquals(0, in.available());
+ }
+
+ private void booleanTest(boolean val) {
+
+ out.reset();
+ out.writeBoolean(val);
+ assertEquals(1, out.size());
+ copyOutputToInput();
+ assertEquals(val, in.readBoolean());
+ assertEquals(0, in.available());
+ }
+
+ public void testBoolean() {
+
+ booleanTest(true);
+ booleanTest(false);
+
+ out.reset();
+ out.writeBoolean(true);
+ out.writeBoolean(false);
+ assertEquals(2, out.size());
+ copyOutputToInput();
+ assertEquals(true, in.readBoolean());
+ assertEquals(false, in.readBoolean());
+ assertEquals(0, in.available());
+
+ out.reset();
+ out.writeBoolean(true);
+ out.writeBoolean(false);
+ out.writeBoolean(true);
+ assertEquals(3, out.size());
+ copyOutputToInput();
+ assertEquals(true, in.readBoolean());
+ assertEquals(false, in.readBoolean());
+ assertEquals(true, in.readBoolean());
+ assertEquals(0, in.available());
+ }
+
+ private void unsignedByteTest(int val) {
+
+ unsignedByteTest(val, val);
+ }
+
+ private void unsignedByteTest(int val, int expected) {
+
+ out.reset();
+ out.writeUnsignedByte(val);
+ assertEquals(1, out.size());
+ copyOutputToInput();
+ assertEquals(expected, in.readUnsignedByte());
+ }
+
+ public void testUnsignedByte() {
+
+ unsignedByteTest(0);
+ unsignedByteTest(1);
+ unsignedByteTest(254);
+ unsignedByteTest(255);
+ unsignedByteTest(256, 0);
+ unsignedByteTest(-1, 255);
+ unsignedByteTest(-2, 254);
+ unsignedByteTest(-255, 1);
+
+ out.reset();
+ out.writeUnsignedByte(0);
+ out.writeUnsignedByte(1);
+ out.writeUnsignedByte(255);
+ assertEquals(3, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readUnsignedByte());
+ assertEquals(1, in.readUnsignedByte());
+ assertEquals(255, in.readUnsignedByte());
+ assertEquals(0, in.available());
+ }
+
+ private void unsignedShortTest(int val) {
+
+ unsignedShortTest(val, val);
+ }
+
+ private void unsignedShortTest(int val, int expected) {
+
+ out.reset();
+ out.writeUnsignedShort(val);
+ assertEquals(2, out.size());
+ copyOutputToInput();
+ assertEquals(expected, in.readUnsignedShort());
+ }
+
+ public void testUnsignedShort() {
+
+ unsignedShortTest(0);
+ unsignedShortTest(1);
+ unsignedShortTest(255);
+ unsignedShortTest(256);
+ unsignedShortTest(257);
+ unsignedShortTest(Short.MAX_VALUE - 1);
+ unsignedShortTest(Short.MAX_VALUE);
+ unsignedShortTest(Short.MAX_VALUE + 1);
+ unsignedShortTest(0xFFFF - 1);
+ unsignedShortTest(0xFFFF);
+ unsignedShortTest(0xFFFF + 1, 0);
+ unsignedShortTest(0x7FFF0000, 0);
+ unsignedShortTest(0xFFFF0000, 0);
+ unsignedShortTest(-1, 0xFFFF);
+ unsignedShortTest(-2, 0xFFFF - 1);
+ unsignedShortTest(-0xFFFF, 1);
+
+ out.reset();
+ out.writeUnsignedShort(0);
+ out.writeUnsignedShort(1);
+ out.writeUnsignedShort(0xFFFF);
+ assertEquals(6, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readUnsignedShort());
+ assertEquals(1, in.readUnsignedShort());
+ assertEquals(0xFFFF, in.readUnsignedShort());
+ assertEquals(0, in.available());
+ }
+
+ private void unsignedIntTest(long val) {
+
+ unsignedIntTest(val, val);
+ }
+
+ private void unsignedIntTest(long val, long expected) {
+
+ out.reset();
+ out.writeUnsignedInt(val);
+ assertEquals(4, out.size());
+ copyOutputToInput();
+ assertEquals(expected, in.readUnsignedInt());
+ }
+
+ public void testUnsignedInt() {
+
+ unsignedIntTest(0L);
+ unsignedIntTest(1L);
+ unsignedIntTest(255L);
+ unsignedIntTest(256L);
+ unsignedIntTest(257L);
+ unsignedIntTest(Short.MAX_VALUE - 1L);
+ unsignedIntTest(Short.MAX_VALUE);
+ unsignedIntTest(Short.MAX_VALUE + 1L);
+ unsignedIntTest(Integer.MAX_VALUE - 1L);
+ unsignedIntTest(Integer.MAX_VALUE);
+ unsignedIntTest(Integer.MAX_VALUE + 1L);
+ unsignedIntTest(0xFFFFFFFFL - 1L);
+ unsignedIntTest(0xFFFFFFFFL);
+ unsignedIntTest(0xFFFFFFFFL + 1L, 0L);
+ unsignedIntTest(0x7FFFFFFF00000000L, 0L);
+ unsignedIntTest(0xFFFFFFFF00000000L, 0L);
+ unsignedIntTest(-1, 0xFFFFFFFFL);
+ unsignedIntTest(-2, 0xFFFFFFFFL - 1L);
+ unsignedIntTest(-0xFFFFFFFFL, 1L);
+
+ out.reset();
+ out.writeUnsignedInt(0L);
+ out.writeUnsignedInt(1L);
+ out.writeUnsignedInt(0xFFFFFFFFL);
+ assertEquals(12, out.size());
+ copyOutputToInput();
+ assertEquals(0L, in.readUnsignedInt());
+ assertEquals(1L, in.readUnsignedInt());
+ assertEquals(0xFFFFFFFFL, in.readUnsignedInt());
+ assertEquals(0L, in.available());
+ }
+
+ private void byteTest(int val) {
+
+ out.reset();
+ out.writeByte(val);
+ assertEquals(1, out.size());
+ copyOutputToInput();
+ assertEquals((byte) val, in.readByte());
+ }
+
+ public void testByte() {
+
+ byteTest(0);
+ byteTest(1);
+ byteTest(-1);
+ byteTest(Byte.MAX_VALUE - 1);
+ byteTest(Byte.MAX_VALUE);
+ byteTest(Byte.MAX_VALUE + 1);
+ byteTest(Byte.MIN_VALUE + 1);
+ byteTest(Byte.MIN_VALUE);
+ byteTest(Byte.MIN_VALUE - 1);
+ byteTest(0x7F);
+ byteTest(0xFF);
+ byteTest(0x7FFF);
+ byteTest(0xFFFF);
+ byteTest(0x7FFFFFFF);
+ byteTest(0xFFFFFFFF);
+
+ out.reset();
+ out.writeByte(0);
+ out.writeByte(1);
+ out.writeByte(-1);
+ assertEquals(3, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readByte());
+ assertEquals(1, in.readByte());
+ assertEquals(-1, in.readByte());
+ assertEquals(0, in.available());
+ }
+
+ private void shortTest(int val) {
+
+ out.reset();
+ out.writeShort(val);
+ assertEquals(2, out.size());
+ copyOutputToInput();
+ assertEquals((short) val, in.readShort());
+ }
+
+ public void testShort() {
+
+ shortTest(0);
+ shortTest(1);
+ shortTest(-1);
+ shortTest(Short.MAX_VALUE - 1);
+ shortTest(Short.MAX_VALUE);
+ shortTest(Short.MAX_VALUE + 1);
+ shortTest(Short.MIN_VALUE + 1);
+ shortTest(Short.MIN_VALUE);
+ shortTest(Short.MIN_VALUE - 1);
+ shortTest(0x7F);
+ shortTest(0xFF);
+ shortTest(0x7FFF);
+ shortTest(0xFFFF);
+ shortTest(0x7FFFFFFF);
+ shortTest(0xFFFFFFFF);
+
+ out.reset();
+ out.writeShort(0);
+ out.writeShort(1);
+ out.writeShort(-1);
+ assertEquals(3 * 2, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readShort());
+ assertEquals(1, in.readShort());
+ assertEquals(-1, in.readShort());
+ assertEquals(0, in.available());
+ }
+
+ private void intTest(int val) {
+
+ out.reset();
+ out.writeInt(val);
+ assertEquals(4, out.size());
+ copyOutputToInput();
+ assertEquals(val, in.readInt());
+ }
+
+ public void testInt() {
+
+ intTest(0);
+ intTest(1);
+ intTest(-1);
+ intTest(Integer.MAX_VALUE - 1);
+ intTest(Integer.MAX_VALUE);
+ intTest(Integer.MAX_VALUE + 1);
+ intTest(Integer.MIN_VALUE + 1);
+ intTest(Integer.MIN_VALUE);
+ intTest(Integer.MIN_VALUE - 1);
+ intTest(0x7F);
+ intTest(0xFF);
+ intTest(0x7FFF);
+ intTest(0xFFFF);
+ intTest(0x7FFFFFFF);
+ intTest(0xFFFFFFFF);
+
+ out.reset();
+ out.writeInt(0);
+ out.writeInt(1);
+ out.writeInt(-1);
+ assertEquals(3 * 4, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readInt());
+ assertEquals(1, in.readInt());
+ assertEquals(-1, in.readInt());
+ assertEquals(0, in.available());
+ }
+
+ private void longTest(long val) {
+
+ out.reset();
+ out.writeLong(val);
+ assertEquals(8, out.size());
+ copyOutputToInput();
+ assertEquals(val, in.readLong());
+ }
+
+ public void testLong() {
+
+ longTest(0);
+ longTest(1);
+ longTest(-1);
+ longTest(Long.MAX_VALUE - 1);
+ longTest(Long.MAX_VALUE);
+ longTest(Long.MAX_VALUE + 1);
+ longTest(Long.MIN_VALUE + 1);
+ longTest(Long.MIN_VALUE);
+ longTest(Long.MIN_VALUE - 1);
+ longTest(0x7F);
+ longTest(0xFF);
+ longTest(0x7FFF);
+ longTest(0xFFFF);
+ longTest(0x7FFFFFFF);
+ longTest(0xFFFFFFFF);
+ longTest(0x7FFFFFFFFFFFFFFFL);
+ longTest(0xFFFFFFFFFFFFFFFFL);
+
+ out.reset();
+ out.writeLong(0);
+ out.writeLong(1);
+ out.writeLong(-1);
+ assertEquals(3 * 8, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readLong());
+ assertEquals(1, in.readLong());
+ assertEquals(-1, in.readLong());
+ assertEquals(0, in.available());
+ }
+
+ private void floatTest(double val) {
+
+ out.reset();
+ out.writeFloat((float) val);
+ assertEquals(4, out.size());
+ copyOutputToInput();
+ if (Double.isNaN(val)) {
+ assertTrue(Float.isNaN(in.readFloat()));
+ } else {
+ assertEquals((float) val, in.readFloat(), 0);
+ }
+ }
+
+ public void testFloat() {
+
+ floatTest(0);
+ floatTest(1);
+ floatTest(-1);
+ floatTest(1.0);
+ floatTest(0.1);
+ floatTest(-1.0);
+ floatTest(-0.1);
+ floatTest(Float.NaN);
+ floatTest(Float.NEGATIVE_INFINITY);
+ floatTest(Float.POSITIVE_INFINITY);
+ floatTest(Short.MAX_VALUE);
+ floatTest(Short.MIN_VALUE);
+ floatTest(Integer.MAX_VALUE);
+ floatTest(Integer.MIN_VALUE);
+ floatTest(Long.MAX_VALUE);
+ floatTest(Long.MIN_VALUE);
+ floatTest(Float.MAX_VALUE);
+ floatTest(Float.MAX_VALUE + 1);
+ floatTest(Float.MIN_VALUE + 1);
+ floatTest(Float.MIN_VALUE);
+ floatTest(Float.MIN_VALUE - 1);
+ floatTest(0x7F);
+ floatTest(0xFF);
+ floatTest(0x7FFF);
+ floatTest(0xFFFF);
+ floatTest(0x7FFFFFFF);
+ floatTest(0xFFFFFFFF);
+ floatTest(0x7FFFFFFFFFFFFFFFL);
+ floatTest(0xFFFFFFFFFFFFFFFFL);
+
+ out.reset();
+ out.writeFloat(0);
+ out.writeFloat(1);
+ out.writeFloat(-1);
+ assertEquals(3 * 4, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readFloat(), 0);
+ assertEquals(1, in.readFloat(), 0);
+ assertEquals(-1, in.readFloat(), 0);
+ assertEquals(0, in.available(), 0);
+ }
+
+ private void doubleTest(double val) {
+
+ out.reset();
+ out.writeDouble(val);
+ assertEquals(8, out.size());
+ copyOutputToInput();
+ if (Double.isNaN(val)) {
+ assertTrue(Double.isNaN(in.readDouble()));
+ } else {
+ assertEquals(val, in.readDouble(), 0);
+ }
+ }
+
+ public void testDouble() {
+
+ doubleTest(0);
+ doubleTest(1);
+ doubleTest(-1);
+ doubleTest(1.0);
+ doubleTest(0.1);
+ doubleTest(-1.0);
+ doubleTest(-0.1);
+ doubleTest(Double.NaN);
+ doubleTest(Double.NEGATIVE_INFINITY);
+ doubleTest(Double.POSITIVE_INFINITY);
+ doubleTest(Short.MAX_VALUE);
+ doubleTest(Short.MIN_VALUE);
+ doubleTest(Integer.MAX_VALUE);
+ doubleTest(Integer.MIN_VALUE);
+ doubleTest(Long.MAX_VALUE);
+ doubleTest(Long.MIN_VALUE);
+ doubleTest(Float.MAX_VALUE);
+ doubleTest(Float.MIN_VALUE);
+ doubleTest(Double.MAX_VALUE - 1);
+ doubleTest(Double.MAX_VALUE);
+ doubleTest(Double.MAX_VALUE + 1);
+ doubleTest(Double.MIN_VALUE + 1);
+ doubleTest(Double.MIN_VALUE);
+ doubleTest(Double.MIN_VALUE - 1);
+ doubleTest(0x7F);
+ doubleTest(0xFF);
+ doubleTest(0x7FFF);
+ doubleTest(0xFFFF);
+ doubleTest(0x7FFFFFFF);
+ doubleTest(0xFFFFFFFF);
+ doubleTest(0x7FFFFFFFFFFFFFFFL);
+ doubleTest(0xFFFFFFFFFFFFFFFFL);
+
+ out.reset();
+ out.writeDouble(0);
+ out.writeDouble(1);
+ out.writeDouble(-1);
+ assertEquals(3 * 8, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readDouble(), 0);
+ assertEquals(1, in.readDouble(), 0);
+ assertEquals(-1, in.readDouble(), 0);
+ assertEquals(0, in.available(), 0);
+ }
+
+ private void sortedFloatTest(double val) {
+
+ out.reset();
+ out.writeSortedFloat((float) val);
+ assertEquals(4, out.size());
+ copyOutputToInput();
+ if (Double.isNaN(val)) {
+ assertTrue(Float.isNaN(in.readSortedFloat()));
+ } else {
+ assertEquals((float) val, in.readSortedFloat(), 0);
+ }
+ }
+
+ public void testSortedFloat() {
+
+ sortedFloatTest(0);
+ sortedFloatTest(1);
+ sortedFloatTest(-1);
+ sortedFloatTest(1.0);
+ sortedFloatTest(0.1);
+ sortedFloatTest(-1.0);
+ sortedFloatTest(-0.1);
+ sortedFloatTest(Float.NaN);
+ sortedFloatTest(Float.NEGATIVE_INFINITY);
+ sortedFloatTest(Float.POSITIVE_INFINITY);
+ sortedFloatTest(Short.MAX_VALUE);
+ sortedFloatTest(Short.MIN_VALUE);
+ sortedFloatTest(Integer.MAX_VALUE);
+ sortedFloatTest(Integer.MIN_VALUE);
+ sortedFloatTest(Long.MAX_VALUE);
+ sortedFloatTest(Long.MIN_VALUE);
+ sortedFloatTest(Float.MAX_VALUE);
+ sortedFloatTest(Float.MAX_VALUE + 1);
+ sortedFloatTest(Float.MIN_VALUE + 1);
+ sortedFloatTest(Float.MIN_VALUE);
+ sortedFloatTest(Float.MIN_VALUE - 1);
+ sortedFloatTest(0x7F);
+ sortedFloatTest(0xFF);
+ sortedFloatTest(0x7FFF);
+ sortedFloatTest(0xFFFF);
+ sortedFloatTest(0x7FFFFFFF);
+ sortedFloatTest(0xFFFFFFFF);
+ sortedFloatTest(0x7FFFFFFFFFFFFFFFL);
+ sortedFloatTest(0xFFFFFFFFFFFFFFFFL);
+
+ out.reset();
+ out.writeSortedFloat(0);
+ out.writeSortedFloat(1);
+ out.writeSortedFloat(-1);
+ assertEquals(3 * 4, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readSortedFloat(), 0);
+ assertEquals(1, in.readSortedFloat(), 0);
+ assertEquals(-1, in.readSortedFloat(), 0);
+ assertEquals(0, in.available(), 0);
+ }
+
+ private void sortedDoubleTest(double val) {
+
+ out.reset();
+ out.writeSortedDouble(val);
+ assertEquals(8, out.size());
+ copyOutputToInput();
+ if (Double.isNaN(val)) {
+ assertTrue(Double.isNaN(in.readSortedDouble()));
+ } else {
+ assertEquals(val, in.readSortedDouble(), 0);
+ }
+ }
+
+ public void testSortedDouble() {
+
+ sortedDoubleTest(0);
+ sortedDoubleTest(1);
+ sortedDoubleTest(-1);
+ sortedDoubleTest(1.0);
+ sortedDoubleTest(0.1);
+ sortedDoubleTest(-1.0);
+ sortedDoubleTest(-0.1);
+ sortedDoubleTest(Double.NaN);
+ sortedDoubleTest(Double.NEGATIVE_INFINITY);
+ sortedDoubleTest(Double.POSITIVE_INFINITY);
+ sortedDoubleTest(Short.MAX_VALUE);
+ sortedDoubleTest(Short.MIN_VALUE);
+ sortedDoubleTest(Integer.MAX_VALUE);
+ sortedDoubleTest(Integer.MIN_VALUE);
+ sortedDoubleTest(Long.MAX_VALUE);
+ sortedDoubleTest(Long.MIN_VALUE);
+ sortedDoubleTest(Float.MAX_VALUE);
+ sortedDoubleTest(Float.MIN_VALUE);
+ sortedDoubleTest(Double.MAX_VALUE - 1);
+ sortedDoubleTest(Double.MAX_VALUE);
+ sortedDoubleTest(Double.MAX_VALUE + 1);
+ sortedDoubleTest(Double.MIN_VALUE + 1);
+ sortedDoubleTest(Double.MIN_VALUE);
+ sortedDoubleTest(Double.MIN_VALUE - 1);
+ sortedDoubleTest(0x7F);
+ sortedDoubleTest(0xFF);
+ sortedDoubleTest(0x7FFF);
+ sortedDoubleTest(0xFFFF);
+ sortedDoubleTest(0x7FFFFFFF);
+ sortedDoubleTest(0xFFFFFFFF);
+ sortedDoubleTest(0x7FFFFFFFFFFFFFFFL);
+ sortedDoubleTest(0xFFFFFFFFFFFFFFFFL);
+
+ out.reset();
+ out.writeSortedDouble(0);
+ out.writeSortedDouble(1);
+ out.writeSortedDouble(-1);
+ assertEquals(3 * 8, out.size());
+ copyOutputToInput();
+ assertEquals(0, in.readSortedDouble(), 0);
+ assertEquals(1, in.readSortedDouble(), 0);
+ assertEquals(-1, in.readSortedDouble(), 0);
+ assertEquals(0, in.available(), 0);
+ }
+
+ private void packedIntTest(int val, int size) {
+
+ out.reset();
+ out.writePackedInt(val);
+ assertEquals(size, out.size());
+ copyOutputToInput();
+ assertEquals(size, in.getPackedIntByteLength());
+ assertEquals(val, in.readPackedInt());
+ }
+
+ public void testPackedInt() {
+
+ /* Exhaustive value testing is in PackedIntTest. */
+ packedIntTest(119, 1);
+ packedIntTest(0xFFFF + 119, 3);
+ packedIntTest(Integer.MAX_VALUE, 5);
+
+ out.reset();
+ out.writePackedInt(119);
+ out.writePackedInt(0xFFFF + 119);
+ out.writePackedInt(Integer.MAX_VALUE);
+ assertEquals(1 + 3 + 5, out.size());
+ copyOutputToInput();
+ assertEquals(119, in.readPackedInt(), 0);
+ assertEquals(0xFFFF + 119, in.readPackedInt(), 0);
+ assertEquals(Integer.MAX_VALUE, in.readPackedInt(), 0);
+ assertEquals(0, in.available(), 0);
+ }
+
+ private void packedLongTest(long val, int size) {
+
+ out.reset();
+ out.writePackedLong(val);
+ assertEquals(size, out.size());
+ copyOutputToInput();
+ assertEquals(size, in.getPackedLongByteLength());
+ assertEquals(val, in.readPackedLong());
+ }
+
+ public void testPackedLong() {
+
+ /* Exhaustive value testing is in PackedIntTest. */
+ packedLongTest(119, 1);
+ packedLongTest(0xFFFFFFFFL + 119, 5);
+ packedLongTest(Long.MAX_VALUE, 9);
+
+ out.reset();
+ out.writePackedLong(119);
+ out.writePackedLong(0xFFFFFFFFL + 119);
+ out.writePackedLong(Long.MAX_VALUE);
+ assertEquals(1 + 5 + 9, out.size());
+ copyOutputToInput();
+ assertEquals(119, in.readPackedLong(), 0);
+ assertEquals(0xFFFFFFFFL + 119, in.readPackedLong(), 0);
+ assertEquals(Long.MAX_VALUE, in.readPackedLong(), 0);
+ assertEquals(0, in.available(), 0);
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/bind/tuple/test/TupleOrderingTest.java b/test/scr024/src/com/sleepycat/bind/tuple/test/TupleOrderingTest.java
new file mode 100644
index 00000000..4a702464
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/bind/tuple/test/TupleOrderingTest.java
@@ -0,0 +1,477 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.bind.tuple.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class TupleOrderingTest extends TestCase {
+
+ private TupleOutput out;
+ private byte[] prevBuf;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(TupleOrderingTest.class);
+ return suite;
+ }
+
+ public TupleOrderingTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+
+ SharedTestUtils.printTestName("TupleOrderingTest." + getName());
+ out = new TupleOutput();
+ prevBuf = null;
+ }
+
+ @Override
+ public void tearDown() {
+
+ /* Ensure that GC can cleanup. */
+ out = null;
+ prevBuf = null;
+ }
+
+ /**
+ * Each tuple written must be strictly less than (by comparison of bytes)
+ * the tuple written just before it. The check() method compares bytes
+ * just written to those written before the previous call to check().
+ */
+ private void check() {
+
+ check(-1);
+ }
+
+ private void check(int dataIndex) {
+
+ byte[] buf = new byte[out.size()];
+ System.arraycopy(out.getBufferBytes(), out.getBufferOffset(),
+ buf, 0, buf.length);
+ if (prevBuf != null) {
+ int errOffset = -1;
+ int len = Math.min(prevBuf.length, buf.length);
+ boolean areEqual = true;
+ for (int i = 0; i < len; i += 1) {
+ int val1 = prevBuf[i] & 0xFF;
+ int val2 = buf[i] & 0xFF;
+ if (val1 < val2) {
+ areEqual = false;
+ break;
+ } else if (val1 > val2) {
+ errOffset = i;
+ break;
+ }
+ }
+ if (areEqual) {
+ if (prevBuf.length < buf.length) {
+ areEqual = false;
+ } else if (prevBuf.length > buf.length) {
+ areEqual = false;
+ errOffset = buf.length + 1;
+ }
+ }
+ if (errOffset != -1 || areEqual) {
+ StringBuffer msg = new StringBuffer();
+ if (errOffset != -1) {
+ msg.append("Left >= right at byte offset " + errOffset);
+ } else if (areEqual) {
+ msg.append("Bytes are equal");
+ } else {
+ throw new IllegalStateException();
+ }
+ msg.append("\nLeft hex bytes: ");
+ for (int i = 0; i < prevBuf.length; i += 1) {
+ msg.append(' ');
+ int val = prevBuf[i] & 0xFF;
+ if ((val & 0xF0) == 0) {
+ msg.append('0');
+ }
+ msg.append(Integer.toHexString(val));
+ }
+ msg.append("\nRight hex bytes:");
+ for (int i = 0; i < buf.length; i += 1) {
+ msg.append(' ');
+ int val = buf[i] & 0xFF;
+ if ((val & 0xF0) == 0) {
+ msg.append('0');
+ }
+ msg.append(Integer.toHexString(val));
+ }
+ if (dataIndex >= 0) {
+ msg.append("\nData index: " + dataIndex);
+ }
+ fail(msg.toString());
+ }
+ }
+ prevBuf = buf;
+ out.reset();
+ }
+
+ private void reset() {
+
+ prevBuf = null;
+ out.reset();
+ }
+
+ public void testString() {
+
+ final String[] DATA = {
+ "", "\u0001", "\u0002",
+ "A", "a", "ab", "b", "bb", "bba",
+ "c", "c\u0001", "d",
+ new String(new char[] { 0x7F }),
+ new String(new char[] { 0x7F, 0 }),
+ new String(new char[] { 0xFF }),
+ new String(new char[] { Character.MAX_VALUE }),
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeString(DATA[i]);
+ check(i);
+ }
+ reset();
+ out.writeString("a");
+ check();
+ out.writeString("a");
+ out.writeString("");
+ check();
+ out.writeString("a");
+ out.writeString("");
+ out.writeString("a");
+ check();
+ out.writeString("a");
+ out.writeString("b");
+ check();
+ out.writeString("aa");
+ check();
+ out.writeString("b");
+ check();
+ }
+
+ public void testFixedString() {
+
+ final char[][] DATA = {
+ {}, {'a'}, {'a', 'b'}, {'b'}, {'b', 'b'}, {0x7F}, {0xFF},
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeString(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testChars() {
+
+ final char[][] DATA = {
+ {}, {0}, {'a'}, {'a', 0}, {'a', 'b'}, {'b'}, {'b', 'b'},
+ {0x7F}, {0x7F, 0}, {0xFF}, {0xFF, 0},
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeChars(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testBytes() {
+
+ final char[][] DATA = {
+ {}, {0}, {'a'}, {'a', 0}, {'a', 'b'}, {'b'}, {'b', 'b'},
+ {0x7F}, {0xFF},
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeBytes(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testBoolean() {
+
+ final boolean[] DATA = {
+ false, true
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeBoolean(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testUnsignedByte() {
+
+ final int[] DATA = {
+ 0, 1, 0x7F, 0xFF
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeUnsignedByte(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testUnsignedShort() {
+
+ final int[] DATA = {
+ 0, 1, 0xFE, 0xFF, 0x800, 0x7FFF, 0xFFFF
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeUnsignedShort(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testUnsignedInt() {
+
+ final long[] DATA = {
+ 0, 1, 0xFE, 0xFF, 0x800, 0x7FFF, 0xFFFF, 0x80000,
+ 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeUnsignedInt(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testByte() {
+
+ final byte[] DATA = {
+ Byte.MIN_VALUE, Byte.MIN_VALUE + 1,
+ -1, 0, 1,
+ Byte.MAX_VALUE - 1, Byte.MAX_VALUE,
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeByte(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testShort() {
+
+ final short[] DATA = {
+ Short.MIN_VALUE, Short.MIN_VALUE + 1,
+ Byte.MIN_VALUE, Byte.MIN_VALUE + 1,
+ -1, 0, 1,
+ Byte.MAX_VALUE - 1, Byte.MAX_VALUE,
+ Short.MAX_VALUE - 1, Short.MAX_VALUE,
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeShort(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testInt() {
+
+ final int[] DATA = {
+ Integer.MIN_VALUE, Integer.MIN_VALUE + 1,
+ Short.MIN_VALUE, Short.MIN_VALUE + 1,
+ Byte.MIN_VALUE, Byte.MIN_VALUE + 1,
+ -1, 0, 1,
+ Byte.MAX_VALUE - 1, Byte.MAX_VALUE,
+ Short.MAX_VALUE - 1, Short.MAX_VALUE,
+ Integer.MAX_VALUE - 1, Integer.MAX_VALUE,
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeInt(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testLong() {
+
+ final long[] DATA = {
+ Long.MIN_VALUE, Long.MIN_VALUE + 1,
+ Integer.MIN_VALUE, Integer.MIN_VALUE + 1,
+ Short.MIN_VALUE, Short.MIN_VALUE + 1,
+ Byte.MIN_VALUE, Byte.MIN_VALUE + 1,
+ -1, 0, 1,
+ Byte.MAX_VALUE - 1, Byte.MAX_VALUE,
+ Short.MAX_VALUE - 1, Short.MAX_VALUE,
+ Integer.MAX_VALUE - 1, Integer.MAX_VALUE,
+ Long.MAX_VALUE - 1, Long.MAX_VALUE,
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeLong(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testFloat() {
+
+ // Only positive floats and doubles are ordered deterministically
+
+ final float[] DATA = {
+ 0, Float.MIN_VALUE, 2 * Float.MIN_VALUE,
+ (float) 0.01, (float) 0.02, (float) 0.99,
+ 1, (float) 1.01, (float) 1.02, (float) 1.99,
+ Byte.MAX_VALUE - 1, Byte.MAX_VALUE,
+ Short.MAX_VALUE - 1, Short.MAX_VALUE,
+ Integer.MAX_VALUE,
+ Long.MAX_VALUE / 2, Long.MAX_VALUE,
+ Float.MAX_VALUE,
+ Float.POSITIVE_INFINITY,
+ Float.NaN,
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeFloat(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testDouble() {
+
+ // Only positive floats and doubles are ordered deterministically
+
+ final double[] DATA = {
+ 0, Double.MIN_VALUE, 2 * Double.MIN_VALUE,
+ 0.001, 0.002, 0.999,
+ 1, 1.001, 1.002, 1.999,
+ Byte.MAX_VALUE - 1, Byte.MAX_VALUE,
+ Short.MAX_VALUE - 1, Short.MAX_VALUE,
+ Integer.MAX_VALUE - 1, Integer.MAX_VALUE,
+ Long.MAX_VALUE / 2, Long.MAX_VALUE,
+ Float.MAX_VALUE, Double.MAX_VALUE,
+ Double.POSITIVE_INFINITY,
+ Double.NaN,
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeDouble(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testSortedFloat() {
+
+ final float[] DATA = {
+ Float.NEGATIVE_INFINITY,
+ (- Float.MAX_VALUE),
+ Long.MIN_VALUE,
+ Long.MIN_VALUE / 2,
+ Integer.MIN_VALUE,
+ Short.MIN_VALUE,
+ Short.MIN_VALUE + 1,
+ Byte.MIN_VALUE,
+ Byte.MIN_VALUE + 1,
+ (float) -1.99,
+ (float) -1.02,
+ (float) -1.01,
+ -1,
+ (float) -0.99,
+ (float) -0.02,
+ (float) -0.01,
+ 2 * (- Float.MIN_VALUE),
+ (- Float.MIN_VALUE),
+ 0,
+ Float.MIN_VALUE,
+ 2 * Float.MIN_VALUE,
+ (float) 0.01,
+ (float) 0.02,
+ (float) 0.99,
+ 1,
+ (float) 1.01,
+ (float) 1.02,
+ (float) 1.99,
+ Byte.MAX_VALUE - 1,
+ Byte.MAX_VALUE,
+ Short.MAX_VALUE - 1,
+ Short.MAX_VALUE,
+ Integer.MAX_VALUE,
+ Long.MAX_VALUE / 2,
+ Long.MAX_VALUE,
+ Float.MAX_VALUE,
+ Float.POSITIVE_INFINITY,
+ Float.NaN,
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeSortedFloat(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testSortedDouble() {
+
+ final double[] DATA = {
+ Double.NEGATIVE_INFINITY,
+ (- Double.MAX_VALUE),
+ (- Float.MAX_VALUE),
+ Long.MIN_VALUE,
+ Long.MIN_VALUE / 2,
+ Integer.MIN_VALUE,
+ Short.MIN_VALUE,
+ Short.MIN_VALUE + 1,
+ Byte.MIN_VALUE,
+ Byte.MIN_VALUE + 1,
+ -1.999,
+ -1.002,
+ -1.001,
+ -1,
+ -0.999,
+ -0.002,
+ -0.001,
+ 2 * (- Double.MIN_VALUE),
+ (- Double.MIN_VALUE),
+ 0,
+ Double.MIN_VALUE,
+ 2 * Double.MIN_VALUE,
+ 0.001,
+ 0.002,
+ 0.999,
+ 1,
+ 1.001,
+ 1.002,
+ 1.999,
+ Byte.MAX_VALUE - 1,
+ Byte.MAX_VALUE,
+ Short.MAX_VALUE - 1,
+ Short.MAX_VALUE,
+ Integer.MAX_VALUE - 1,
+ Integer.MAX_VALUE,
+ Long.MAX_VALUE / 2,
+ Long.MAX_VALUE,
+ Float.MAX_VALUE,
+ Double.MAX_VALUE,
+ Double.POSITIVE_INFINITY,
+ Double.NaN,
+ };
+ for (int i = 0; i < DATA.length; i += 1) {
+ out.writeSortedDouble(DATA[i]);
+ check(i);
+ }
+ }
+
+ public void testPackedIntAndLong() {
+ /* Only packed int/long values from 0 to 630 are ordered correctly */
+ for (int i = 0; i <= 630; i += 1) {
+ out.writePackedInt(i);
+ check(i);
+ }
+ reset();
+ for (int i = 0; i <= 630; i += 1) {
+ out.writePackedLong(i);
+ check(i);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/KeyRangeTest.java b/test/scr024/src/com/sleepycat/collections/KeyRangeTest.java
new file mode 100644
index 00000000..aeba1bc1
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/KeyRangeTest.java
@@ -0,0 +1,440 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.ByteArrayBinding;
+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.EnvironmentConfig;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.util.keyrange.KeyRange;
+import com.sleepycat.util.keyrange.KeyRangeException;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class KeyRangeTest extends TestCase {
+
+ private static boolean VERBOSE = false;
+
+ private static final byte FF = (byte) 0xFF;
+
+ private static final byte[][] KEYS = {
+ /* 0 */ {1},
+ /* 1 */ {FF},
+ /* 2 */ {FF, 0},
+ /* 3 */ {FF, 0x7F},
+ /* 4 */ {FF, FF},
+ /* 5 */ {FF, FF, 0},
+ /* 6 */ {FF, FF, 0x7F},
+ /* 7 */ {FF, FF, FF},
+ };
+ private static byte[][] EXTREME_KEY_BYTES = {
+ /* 0 */ {0},
+ /* 1 */ {FF, FF, FF, FF},
+ };
+
+ private Environment env;
+ private Database store;
+ private DataView view;
+ private DataCursor cursor;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+
+ return new TestSuite(KeyRangeTest.class);
+ }
+
+ public KeyRangeTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+ SharedTestUtils.printTestName(SharedTestUtils.qualifiedTestName(this));
+ }
+
+ private void openDb(Comparator<byte []> comparator)
+ throws Exception {
+
+ File dir = SharedTestUtils.getNewDir();
+ ByteArrayBinding dataBinding = new ByteArrayBinding();
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ DbCompat.setInitializeCache(envConfig, true);
+ env = new Environment(dir, envConfig);
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ DbCompat.setTypeBtree(dbConfig);
+ dbConfig.setAllowCreate(true);
+ if (comparator != null) {
+ DbCompat.setBtreeComparator(dbConfig, comparator);
+ }
+ store = DbCompat.testOpenDatabase
+ (env, null, "test.db", null, dbConfig);
+ view = new DataView(store, dataBinding, dataBinding, null, true, null);
+ }
+
+ private void closeDb()
+ throws Exception {
+
+ store.close();
+ store = null;
+ env.close();
+ env = null;
+ }
+
+ @Override
+ public void tearDown() {
+ try {
+ if (store != null) {
+ store.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Exception ignored during close: " + e);
+ }
+ try {
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Exception ignored during close: " + e);
+ }
+ /* Ensure that GC can cleanup. */
+ env = null;
+ store = null;
+ view = null;
+ cursor = null;
+ }
+
+ public void testScan() throws Exception {
+ openDb(null);
+ doScan(false);
+ closeDb();
+ }
+
+ public void testScanComparator() throws Exception {
+ openDb(new ReverseComparator());
+ doScan(true);
+ closeDb();
+ }
+
+ private void doScan(boolean reversed) throws Exception {
+
+ byte[][] keys = new byte[KEYS.length][];
+ final int end = KEYS.length - 1;
+ cursor = new DataCursor(view, true);
+ for (int i = 0; i <= end; i++) {
+ keys[i] = KEYS[i];
+ cursor.put(keys[i], KEYS[i], null, false);
+ }
+ cursor.close();
+ byte[][] extremeKeys = new byte[EXTREME_KEY_BYTES.length][];
+ for (int i = 0; i < extremeKeys.length; i++) {
+ extremeKeys[i] = EXTREME_KEY_BYTES[i];
+ }
+
+ // with empty range
+
+ cursor = new DataCursor(view, false);
+ expectRange(KEYS, 0, end, reversed);
+ cursor.close();
+
+ // begin key only, inclusive
+
+ for (int i = 0; i <= end; i++) {
+ cursor = newCursor(view, keys[i], true, null, false, reversed);
+ expectRange(KEYS, i, end, reversed);
+ cursor.close();
+ }
+
+ // begin key only, exclusive
+
+ for (int i = 0; i <= end; i++) {
+ cursor = newCursor(view, keys[i], false, null, false, reversed);
+ expectRange(KEYS, i + 1, end, reversed);
+ cursor.close();
+ }
+
+ // end key only, inclusive
+
+ for (int i = 0; i <= end; i++) {
+ cursor = newCursor(view, null, false, keys[i], true, reversed);
+ expectRange(KEYS, 0, i, reversed);
+ cursor.close();
+ }
+
+ // end key only, exclusive
+
+ for (int i = 0; i <= end; i++) {
+ cursor = newCursor(view, null, false, keys[i], false, reversed);
+ expectRange(KEYS, 0, i - 1, reversed);
+ cursor.close();
+ }
+
+ // begin and end keys, inclusive and exclusive
+
+ for (int i = 0; i <= end; i++) {
+ for (int j = i; j <= end; j++) {
+ // begin inclusive, end inclusive
+
+ cursor = newCursor(view, keys[i], true, keys[j],
+ true, reversed);
+ expectRange(KEYS, i, j, reversed);
+ cursor.close();
+
+ // begin inclusive, end exclusive
+
+ cursor = newCursor(view, keys[i], true, keys[j],
+ false, reversed);
+ expectRange(KEYS, i, j - 1, reversed);
+ cursor.close();
+
+ // begin exclusive, end inclusive
+
+ cursor = newCursor(view, keys[i], false, keys[j],
+ true, reversed);
+ expectRange(KEYS, i + 1, j, reversed);
+ cursor.close();
+
+ // begin exclusive, end exclusive
+
+ cursor = newCursor(view, keys[i], false, keys[j],
+ false, reversed);
+ expectRange(KEYS, i + 1, j - 1, reversed);
+ cursor.close();
+ }
+ }
+
+ // single key range
+
+ for (int i = 0; i <= end; i++) {
+ cursor = new DataCursor(view, false, keys[i]);
+ expectRange(KEYS, i, i, reversed);
+ cursor.close();
+ }
+
+ // start with lower extreme (before any existing key)
+
+ cursor = newCursor(view, extremeKeys[0], true, null, false, reversed);
+ expectRange(KEYS, 0, end, reversed);
+ cursor.close();
+
+ // start with higher extreme (after any existing key)
+
+ cursor = newCursor(view, null, false, extremeKeys[1], true, reversed);
+ expectRange(KEYS, 0, end, reversed);
+ cursor.close();
+ }
+
+ private DataCursor newCursor(DataView view,
+ Object beginKey, boolean beginInclusive,
+ Object endKey, boolean endInclusive,
+ boolean reversed)
+ throws Exception {
+
+ if (reversed) {
+ return new DataCursor(view, false,
+ endKey, endInclusive,
+ beginKey, beginInclusive);
+ } else {
+ return new DataCursor(view, false,
+ beginKey, beginInclusive,
+ endKey, endInclusive);
+ }
+ }
+
+ private void expectRange(byte[][] bytes, int first, int last,
+ boolean reversed)
+ throws DatabaseException {
+
+ int i;
+ boolean init;
+ for (init = true, i = first;; i++, init = false) {
+ if (checkRange(bytes, first, last, i <= last,
+ reversed, !reversed, init, i)) {
+ break;
+ }
+ }
+ for (init = true, i = last;; i--, init = false) {
+ if (checkRange(bytes, first, last, i >= first,
+ reversed, reversed, init, i)) {
+ break;
+ }
+ }
+ }
+
+ private boolean checkRange(byte[][] bytes, int first, int last,
+ boolean inRange, boolean reversed,
+ boolean forward, boolean init,
+ int i)
+ throws DatabaseException {
+
+ OperationStatus s;
+ if (forward) {
+ if (init) {
+ s = cursor.getFirst(false);
+ } else {
+ s = cursor.getNext(false);
+ }
+ } else {
+ if (init) {
+ s = cursor.getLast(false);
+ } else {
+ s = cursor.getPrev(false);
+ }
+ }
+
+ String msg = " " + (forward ? "next" : "prev") + " i=" + i +
+ " first=" + first + " last=" + last +
+ (reversed ? " reversed" : " not reversed");
+
+ // check that moving past ends doesn't move the cursor
+ if (s == OperationStatus.SUCCESS && i == first) {
+ OperationStatus s2 = reversed ? cursor.getNext(false)
+ : cursor.getPrev(false);
+ assertEquals(msg, OperationStatus.NOTFOUND, s2);
+ }
+ if (s == OperationStatus.SUCCESS && i == last) {
+ OperationStatus s2 = reversed ? cursor.getPrev(false)
+ : cursor.getNext(false);
+ assertEquals(msg, OperationStatus.NOTFOUND, s2);
+ }
+
+ byte[] val = (s == OperationStatus.SUCCESS)
+ ? ((byte[]) cursor.getCurrentValue())
+ : null;
+
+ if (inRange) {
+ assertNotNull("RangeNotFound" + msg, val);
+
+ if (!Arrays.equals(val, bytes[i])){
+ printBytes(val);
+ printBytes(bytes[i]);
+ fail("RangeKeyNotEqual" + msg);
+ }
+ if (VERBOSE) {
+ System.out.println("GotRange" + msg);
+ }
+ return false;
+ } else {
+ assertEquals("RangeExceeded" + msg, OperationStatus.NOTFOUND, s);
+ return true;
+ }
+ }
+
+ private void printBytes(byte[] bytes) {
+
+ for (int i = 0; i < bytes.length; i += 1) {
+ System.out.print(Integer.toHexString(bytes[i] & 0xFF));
+ System.out.print(' ');
+ }
+ System.out.println();
+ }
+
+ public void testSubRanges() {
+
+ DatabaseEntry begin = new DatabaseEntry();
+ DatabaseEntry begin2 = new DatabaseEntry();
+ DatabaseEntry end = new DatabaseEntry();
+ DatabaseEntry end2 = new DatabaseEntry();
+ KeyRange range = new KeyRange(null);
+ KeyRange range2 = null;
+
+ /* Base range [1, 2] */
+ begin.setData(new byte[] { 1 });
+ end.setData(new byte[] { 2 });
+ range = range.subRange(begin, true, end, true);
+
+ /* Subrange (0, 1] is invalid **. */
+ begin2.setData(new byte[] { 0 });
+ end2.setData(new byte[] { 1 });
+ try {
+ range2 = range.subRange(begin2, false, end2, true);
+ fail();
+ } catch (KeyRangeException expected) {}
+
+ /* Subrange [1, 3) is invalid. */
+ begin2.setData(new byte[] { 1 });
+ end2.setData(new byte[] { 3 });
+ try {
+ range2 = range.subRange(begin2, true, end2, false);
+ fail();
+ } catch (KeyRangeException expected) {}
+
+ /* Subrange [2, 2] is valid. */
+ begin2.setData(new byte[] { 2 });
+ end2.setData(new byte[] { 2 });
+ range2 = range.subRange(begin2, true, end2, true);
+
+ /* Subrange [0, 1] is invalid. */
+ begin2.setData(new byte[] { 0 });
+ end2.setData(new byte[] { 1 });
+ try {
+ range2 = range.subRange(begin2, true, end2, true);
+ fail();
+ } catch (KeyRangeException expected) {}
+
+ /* Subrange (0, 3] is invalid. */
+ begin2.setData(new byte[] { 0 });
+ end2.setData(new byte[] { 3 });
+ try {
+ range2 = range.subRange(begin2, false, end2, true);
+ fail();
+ } catch (KeyRangeException expected) {}
+
+ /* Subrange [3, 3) is invalid. */
+ begin2.setData(new byte[] { 3 });
+ end2.setData(new byte[] { 3 });
+ try {
+ range2 = range.subRange(begin2, true, end2, false);
+ fail();
+ } catch (KeyRangeException expected) {}
+ }
+
+ @SuppressWarnings("serial")
+ public static class ReverseComparator implements Comparator<byte[]>,
+ Serializable {
+ public int compare(byte[] d1, byte[] d2) {
+ int cmp = KeyRange.compareBytes(d1, 0, d1.length,
+ d2, 0, d2.length);
+ if (cmp < 0) {
+ return 1;
+ } else if (cmp > 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/CollectionTest.java b/test/scr024/src/com/sleepycat/collections/test/CollectionTest.java
new file mode 100644
index 00000000..e690c012
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/CollectionTest.java
@@ -0,0 +1,3048 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentMap;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.collections.MapEntryParameter;
+import com.sleepycat.collections.StoredCollection;
+import com.sleepycat.collections.StoredCollections;
+import com.sleepycat.collections.StoredContainer;
+import com.sleepycat.collections.StoredEntrySet;
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.StoredKeySet;
+import com.sleepycat.collections.StoredList;
+import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.StoredSortedEntrySet;
+import com.sleepycat.collections.StoredSortedKeySet;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.StoredSortedValueSet;
+import com.sleepycat.collections.StoredValueSet;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class CollectionTest extends TestCase {
+
+ private static final int NONE = 0;
+ private static final int SUB = 1;
+ private static final int HEAD = 2;
+ private static final int TAIL = 3;
+
+ /*
+ * For long tests we permute testStoredIterator to test both StoredIterator
+ * and BlockIterator. When testing BlockIterator, we permute the maxKey
+ * over the array values below. BlockIterator's block size is 10. So we
+ * test below the block size (6), at the block size (10), and above it (14
+ * and 22).
+ */
+ private static final int DEFAULT_MAX_KEY = 6;
+ private static final int[] MAX_KEYS = {6, 10, 14, 22};
+
+ private boolean testStoredIterator;
+ private int maxKey; /* Must be a multiple of 2. */
+ private int beginKey = 1;
+ private int endKey;
+
+ private Environment env;
+ private Database store;
+ private Database index;
+ private final boolean isEntityBinding;
+ private final boolean isAutoCommit;
+ private TestStore testStore;
+ private String testName;
+ private final EntryBinding keyBinding;
+ private final EntryBinding valueBinding;
+ private final EntityBinding entityBinding;
+ private TransactionRunner readRunner;
+ private TransactionRunner writeRunner;
+ private TransactionRunner writeIterRunner;
+ private TestEnv testEnv;
+
+ private StoredMap map;
+ private StoredMap imap; // insertable map (primary store for indexed map)
+ private StoredSortedMap smap; // sorted map (null or equal to map)
+ private StoredMap saveMap;
+ private StoredSortedMap saveSMap;
+ private int rangeType;
+ private StoredList list;
+ private StoredList ilist; // insertable list (primary store for index list)
+ private StoredList saveList;
+ private StoredKeySet keySet;
+ private StoredValueSet valueSet;
+
+ /**
+ * Runs a command line collection test.
+ * @see #usage
+ */
+ public static void main(String[] args) {
+ if (args.length == 1 &&
+ (args[0].equals("-h") || args[0].equals("-help"))) {
+ usage();
+ } else {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite(args));
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+ }
+
+ private static void usage() {
+
+ System.out.println(
+ "Usage: java com.sleepycat.collections.test.CollectionTest\n" +
+ " -h | -help\n" +
+ " [testName]...\n" +
+ " where testName has the format:\n" +
+ " <env>-<store>-{entity|value}\n" +
+ " <env> is:\n" +
+ " bdb | cdb | txn\n" +
+ " <store> is:\n" +
+ " btree-uniq | btree-dup | btree-dupsort | btree-recnum |\n" +
+ " hash-uniq | hash-dup | hash-dupsort |\n" +
+ " queue | recno | recno-renum\n" +
+ " For example: bdb-btree-uniq-entity\n" +
+ " If no arguments are given then all tests are run.");
+ System.exit(2);
+ }
+
+ public static Test suite() {
+ return suite(null);
+ }
+
+ static Test suite(String[] args) {
+ if (SharedTestUtils.runLongTests()) {
+ TestSuite suite = new TestSuite();
+
+ /* StoredIterator tests. */
+ permuteTests(args, suite, true, DEFAULT_MAX_KEY);
+
+ /* BlockIterator tests with different maxKey values. */
+ for (int i = 0; i < MAX_KEYS.length; i += 1) {
+ permuteTests(args, suite, false, MAX_KEYS[i]);
+ }
+
+ return suite;
+ } else {
+ return baseSuite(args);
+ }
+ }
+
+ private static void permuteTests(String[] args,
+ TestSuite suite,
+ boolean storedIter,
+ int maxKey) {
+ TestSuite baseTests = baseSuite(args);
+ Enumeration e = baseTests.tests();
+ while (e.hasMoreElements()) {
+ CollectionTest t = (CollectionTest) e.nextElement();
+ t.setParams(storedIter, maxKey);
+ suite.addTest(t);
+ }
+ }
+
+ private static TestSuite baseSuite(String[] args) {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ for (int j = 0; j < TestStore.ALL.length; j += 1) {
+ for (int k = 0; k < 2; k += 1) {
+ boolean entityBinding = (k != 0);
+
+ addTest(args, suite, new CollectionTest(
+ TestEnv.ALL[i], TestStore.ALL[j],
+ entityBinding, false));
+
+ if (TestEnv.ALL[i].isTxnMode()) {
+ addTest(args, suite, new CollectionTest(
+ TestEnv.ALL[i], TestStore.ALL[j],
+ entityBinding, true));
+ }
+ }
+ }
+ }
+ return suite;
+ }
+
+ private static void addTest(String[] args, TestSuite suite,
+ CollectionTest test) {
+
+ if (args == null || args.length == 0) {
+ suite.addTest(test);
+ } else {
+ for (int t = 0; t < args.length; t += 1) {
+ if (args[t].equals(test.testName)) {
+ suite.addTest(test);
+ break;
+ }
+ }
+ }
+ }
+
+ public CollectionTest(TestEnv testEnv, TestStore testStore,
+ boolean isEntityBinding, boolean isAutoCommit) {
+
+ super(null);
+
+ this.testEnv = testEnv;
+ this.testStore = testStore;
+ this.isEntityBinding = isEntityBinding;
+ this.isAutoCommit = isAutoCommit;
+
+ keyBinding = testStore.getKeyBinding();
+ valueBinding = testStore.getValueBinding();
+ entityBinding = testStore.getEntityBinding();
+
+ setParams(false, DEFAULT_MAX_KEY);
+ }
+
+ private void setParams(boolean storedIter, int maxKey) {
+
+ this.testStoredIterator = storedIter;
+ this.maxKey = maxKey;
+ this.endKey = maxKey;
+
+ testName = testEnv.getName() + '-' + testStore.getName() +
+ (isEntityBinding ? "-entity" : "-value") +
+ (isAutoCommit ? "-autoCommit" : "") +
+ (testStoredIterator ? "-storedIter" : "") +
+ ((maxKey != DEFAULT_MAX_KEY) ? ("-maxKey-" + maxKey) : "");
+ }
+
+ @Override
+ public void tearDown() {
+ setName(testName);
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ SharedTestUtils.printTestName(SharedTestUtils.qualifiedTestName(this));
+ try {
+ env = testEnv.open(testName);
+
+ // For testing auto-commit, use a normal (transactional) runner for
+ // all reading and for writing via an iterator, and a do-nothing
+ // runner for writing via collections; if auto-commit is tested,
+ // the per-collection auto-commit property will be set elsewhere.
+ //
+ TransactionRunner normalRunner = newTransactionRunner(env);
+ normalRunner.setAllowNestedTransactions(
+ DbCompat.NESTED_TRANSACTIONS);
+ TransactionRunner nullRunner = new NullTransactionRunner(env);
+ readRunner = nullRunner;
+ if (isAutoCommit) {
+ writeRunner = nullRunner;
+ writeIterRunner = testStoredIterator ? normalRunner
+ : nullRunner;
+ } else {
+ writeRunner = normalRunner;
+ writeIterRunner = normalRunner;
+ }
+
+ store = testStore.open(env, "unindexed.db");
+ testUnindexed();
+ store.close();
+ store = null;
+
+ TestStore indexOf = testStore.getIndexOf();
+ if (indexOf != null) {
+ store = indexOf.open(env, "indexed.db");
+ index = testStore.openIndex(store, "index.db");
+ testIndexed();
+ index.close();
+ index = null;
+ store.close();
+ store = null;
+ }
+ env.close();
+ env = null;
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ } finally {
+ if (index != null) {
+ try {
+ index.close();
+ } catch (Exception e) {
+ }
+ }
+ if (store != null) {
+ try {
+ store.close();
+ } catch (Exception e) {
+ }
+ }
+ if (env != null) {
+ try {
+ env.close();
+ } catch (Exception e) {
+ }
+ }
+ /* Ensure that GC can cleanup. */
+ index = null;
+ store = null;
+ env = null;
+ readRunner = null;
+ writeRunner = null;
+ writeIterRunner = null;
+ map = null;
+ imap = null;
+ smap = null;
+ saveMap = null;
+ saveSMap = null;
+ list = null;
+ ilist = null;
+ saveList = null;
+ keySet = null;
+ valueSet = null;
+ testEnv = null;
+ testStore = null;
+ }
+ }
+
+ /**
+ * Is overridden in XACollectionTest.
+ * @throws DatabaseException from subclasses.
+ */
+ protected TransactionRunner newTransactionRunner(Environment env)
+ throws DatabaseException {
+
+ return new TransactionRunner(env);
+ }
+
+ void testCreation(StoredContainer cont, int expectSize) {
+ assertEquals(index != null, cont.isSecondary());
+ assertEquals(testStore.isOrdered(), cont.isOrdered());
+ assertEquals(testStore.areKeyRangesAllowed(),
+ cont.areKeyRangesAllowed());
+ assertEquals(testStore.areKeysRenumbered(), cont.areKeysRenumbered());
+ assertEquals(testStore.areDuplicatesAllowed(),
+ cont.areDuplicatesAllowed());
+ assertEquals(testEnv.isTxnMode(), cont.isTransactional());
+ assertEquals(expectSize, cont.size());
+ }
+
+ void testMapCreation(ConcurrentMap map) {
+ assertTrue(map.values() instanceof Set);
+ assertEquals(testStore.areKeyRangesAllowed(),
+ map.keySet() instanceof SortedSet);
+ assertEquals(testStore.areKeyRangesAllowed(),
+ map.entrySet() instanceof SortedSet);
+ assertEquals(testStore.areKeyRangesAllowed() && isEntityBinding,
+ map.values() instanceof SortedSet);
+ }
+
+ void testUnindexed()
+ throws Exception {
+
+ // create primary map
+ if (testStore.areKeyRangesAllowed()) {
+ if (isEntityBinding) {
+ smap = new StoredSortedMap(store, keyBinding,
+ entityBinding,
+ testStore.getKeyAssigner());
+ valueSet = new StoredSortedValueSet(store, entityBinding,
+ true);
+ } else {
+ smap = new StoredSortedMap(store, keyBinding,
+ valueBinding,
+ testStore.getKeyAssigner());
+ // sorted value set is not possible since key cannot be derived
+ // for performing subSet, etc.
+ }
+ keySet = new StoredSortedKeySet(store, keyBinding, true);
+ map = smap;
+ } else {
+ if (isEntityBinding) {
+ map = new StoredMap(store, keyBinding, entityBinding,
+ testStore.getKeyAssigner());
+ valueSet = new StoredValueSet(store, entityBinding, true);
+ } else {
+ map = new StoredMap(store, keyBinding, valueBinding,
+ testStore.getKeyAssigner());
+ valueSet = new StoredValueSet(store, valueBinding, true);
+ }
+ smap = null;
+ keySet = new StoredKeySet(store, keyBinding, true);
+ }
+ imap = map;
+
+ // create primary list
+ if (testStore.hasRecNumAccess()) {
+ if (isEntityBinding) {
+ ilist = new StoredList(store, entityBinding,
+ testStore.getKeyAssigner());
+ } else {
+ ilist = new StoredList(store, valueBinding,
+ testStore.getKeyAssigner());
+ }
+ list = ilist;
+ } else {
+ try {
+ if (isEntityBinding) {
+ ilist = new StoredList(store, entityBinding,
+ testStore.getKeyAssigner());
+ } else {
+ ilist = new StoredList(store, valueBinding,
+ testStore.getKeyAssigner());
+ }
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ testCreation(map, 0);
+ if (list != null) {
+ testCreation(list, 0);
+ }
+ testMapCreation(map);
+ addAll();
+ testAll();
+ }
+
+ void testIndexed()
+ throws Exception {
+
+ // create primary map
+ if (isEntityBinding) {
+ map = new StoredMap(store, keyBinding, entityBinding,
+ testStore.getKeyAssigner());
+ } else {
+ map = new StoredMap(store, keyBinding, valueBinding,
+ testStore.getKeyAssigner());
+ }
+ imap = map;
+ smap = null;
+ // create primary list
+ if (testStore.hasRecNumAccess()) {
+ if (isEntityBinding) {
+ list = new StoredList(store, entityBinding,
+ testStore.getKeyAssigner());
+ } else {
+ list = new StoredList(store, valueBinding,
+ testStore.getKeyAssigner());
+ }
+ ilist = list;
+ }
+
+ addAll();
+ readAll();
+
+ // create indexed map (keySet/valueSet)
+ if (testStore.areKeyRangesAllowed()) {
+ if (isEntityBinding) {
+ map = smap = new StoredSortedMap(index, keyBinding,
+ entityBinding, true);
+ valueSet = new StoredSortedValueSet(index, entityBinding,
+ true);
+ } else {
+ map = smap = new StoredSortedMap(index, keyBinding,
+ valueBinding, true);
+ // sorted value set is not possible since key cannot be derived
+ // for performing subSet, etc.
+ }
+ keySet = new StoredSortedKeySet(index, keyBinding, true);
+ } else {
+ if (isEntityBinding) {
+ map = new StoredMap(index, keyBinding, entityBinding, true);
+ valueSet = new StoredValueSet(index, entityBinding, true);
+ } else {
+ map = new StoredMap(index, keyBinding, valueBinding, true);
+ valueSet = new StoredValueSet(index, valueBinding, true);
+ }
+ smap = null;
+ keySet = new StoredKeySet(index, keyBinding, true);
+ }
+
+ // create indexed list
+ if (testStore.hasRecNumAccess()) {
+ if (isEntityBinding) {
+ list = new StoredList(index, entityBinding, true);
+ } else {
+ list = new StoredList(index, valueBinding, true);
+ }
+ } else {
+ try {
+ if (isEntityBinding) {
+ list = new StoredList(index, entityBinding, true);
+ } else {
+ list = new StoredList(index, valueBinding, true);
+ }
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ testCreation(map, maxKey);
+ testCreation((StoredContainer) map.values(), maxKey);
+ testCreation((StoredContainer) map.keySet(), maxKey);
+ testCreation((StoredContainer) map.entrySet(), maxKey);
+ if (list != null) {
+ testCreation(list, maxKey);
+ }
+ testMapCreation(map);
+ testAll();
+ }
+
+ void testAll()
+ throws Exception {
+
+ checkKeySetAndValueSet();
+ readAll();
+ updateAll();
+ readAll();
+ if (!map.areKeysRenumbered()) {
+ removeOdd();
+ readEven();
+ addOdd();
+ readAll();
+ removeOddIter();
+ readEven();
+ if (imap.areDuplicatesAllowed()) {
+ addOddDup();
+ } else {
+ addOdd();
+ }
+ readAll();
+ removeOddEntry();
+ readEven();
+ addOdd();
+ readAll();
+ if (isEntityBinding) {
+ removeOddEntity();
+ readEven();
+ addOddEntity();
+ readAll();
+ }
+ bulkOperations();
+ }
+ if (isListAddAllowed()) {
+ removeOddList();
+ readEvenList();
+ addOddList();
+ readAll();
+ if (!isEntityBinding) {
+ removeOddListValue();
+ readEvenList();
+ addOddList();
+ readAll();
+ }
+ }
+ if (list != null) {
+ bulkListOperations();
+ } else {
+ listOperationsNotAllowed();
+ }
+ if (smap != null) {
+ readWriteRange(SUB, 1, 1);
+ readWriteRange(HEAD, 1, 1);
+ readWriteRange(SUB, 1, maxKey);
+ readWriteRange(HEAD, 1, maxKey);
+ readWriteRange(TAIL, 1, maxKey);
+ readWriteRange(SUB, 1, 3);
+ readWriteRange(HEAD, 1, 3);
+ readWriteRange(SUB, 2, 2);
+ readWriteRange(SUB, 2, maxKey);
+ readWriteRange(TAIL, 2, maxKey);
+ readWriteRange(SUB, maxKey, maxKey);
+ readWriteRange(TAIL, maxKey, maxKey);
+ readWriteRange(SUB, maxKey + 1, maxKey + 1);
+ readWriteRange(TAIL, maxKey + 1, maxKey + 1);
+ readWriteRange(SUB, 0, 0);
+ readWriteRange(HEAD, 0, 0);
+ }
+ updateAll();
+ readAll();
+ if (map.areDuplicatesAllowed()) {
+ readWriteDuplicates();
+ readAll();
+ } else {
+ duplicatesNotAllowed();
+ readAll();
+ }
+ if (testEnv.isCdbMode()) {
+ testCdbLocking();
+ }
+ removeAll();
+ if (!map.areKeysRenumbered()) {
+ testConcurrentMap();
+ }
+ if (isListAddAllowed()) {
+ testIterAddList();
+ clearAll();
+ }
+ if (imap.areDuplicatesAllowed()) {
+ testIterAddDuplicates();
+ clearAll();
+ }
+ if (isListAddAllowed()) {
+ addAllList();
+ readAll();
+ removeAllList();
+ }
+ appendAll();
+ }
+
+ void checkKeySetAndValueSet() {
+
+ // use bulk operations to check that explicitly constructed
+ // keySet/valueSet are equivalent
+ assertEquals(keySet, imap.keySet());
+ if (valueSet != null) {
+ assertEquals(valueSet, imap.values());
+ }
+ }
+
+ Iterator iterator(Collection storedCollection) {
+
+ if (testStoredIterator) {
+ return ((StoredCollection) storedCollection).storedIterator();
+ } else {
+ return storedCollection.iterator();
+ }
+ }
+
+ void addAll()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertTrue(imap.isEmpty());
+ Iterator iter = iterator(imap.entrySet());
+ try {
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ assertEquals(0, imap.keySet().toArray().length);
+ assertEquals(0, imap.keySet().toArray(new Object[0]).length);
+ assertEquals(0, imap.entrySet().toArray().length);
+ assertEquals(0, imap.entrySet().toArray(new Object[0]).length);
+ assertEquals(0, imap.values().toArray().length);
+ assertEquals(0, imap.values().toArray(new Object[0]).length);
+
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ assertTrue(!imap.keySet().contains(key));
+ assertTrue(!imap.values().contains(val));
+ assertNull(imap.put(key, val));
+ assertEquals(val, imap.get(key));
+ assertTrue(imap.keySet().contains(key));
+ assertTrue(imap.values().contains(val));
+ assertTrue(imap.duplicates(key).contains(val));
+ if (!imap.areDuplicatesAllowed()) {
+ assertEquals(val, imap.put(key, val));
+ }
+ checkDupsSize(1, imap.duplicates(key));
+ }
+ assertTrue(!imap.isEmpty());
+ }
+ });
+ }
+
+ void appendAll()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertTrue(imap.isEmpty());
+
+ TestKeyAssigner keyAssigner = testStore.getKeyAssigner();
+ if (keyAssigner != null) {
+ keyAssigner.reset();
+ }
+
+ for (int i = beginKey; i <= endKey; i += 1) {
+ boolean useList = (i & 1) == 0;
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ if (keyAssigner != null) {
+ if (useList && ilist != null) {
+ assertEquals(i - 1, ilist.append(val));
+ } else {
+ assertEquals(key, imap.append(val));
+ }
+ assertEquals(val, imap.get(key));
+ } else {
+ Long recnoKey;
+ if (useList && ilist != null) {
+ recnoKey = new Long(ilist.append(val) + 1);
+ } else {
+ recnoKey = (Long) imap.append(val);
+ }
+ assertNotNull(recnoKey);
+ Object recnoVal;
+ if (isEntityBinding) {
+ recnoVal = makeEntity(recnoKey.intValue(), i);
+ } else {
+ recnoVal = val;
+ }
+ assertEquals(recnoVal, imap.get(recnoKey));
+ }
+ }
+ }
+ });
+ }
+
+ void updateAll()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ if (!imap.areDuplicatesAllowed()) {
+ assertEquals(val, imap.put(key, val));
+ }
+ if (isEntityBinding) {
+ assertTrue(!imap.values().add(val));
+ }
+ checkDupsSize(1, imap.duplicates(key));
+ if (ilist != null) {
+ int idx = i - 1;
+ assertEquals(val, ilist.set(idx, val));
+ }
+ }
+ updateIter(map.entrySet());
+ updateIter(map.values());
+ if (beginKey <= endKey) {
+ ListIterator iter = (ListIterator) iterator(map.keySet());
+ try {
+ assertNotNull(iter.next());
+ iter.set(makeKey(beginKey));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ if (list != null) {
+ updateIter(list);
+ }
+ }
+ });
+ }
+
+ void updateIter(final Collection coll)
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ ListIterator iter = (ListIterator) iterator(coll);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Object obj = iter.next();
+ if (map.isOrdered()) {
+ assertEquals(i, intIter(coll, obj));
+ }
+ if (index != null) {
+ try {
+ setValuePlusOne(iter, obj);
+ fail();
+ } catch (UnsupportedOperationException e) {}
+ } else if
+ (((StoredCollection) coll).areDuplicatesOrdered()) {
+ try {
+ setValuePlusOne(iter, obj);
+ fail();
+ } catch (RuntimeException e) {
+ Exception e2 = ExceptionUnwrapper.unwrap(e);
+ assertTrue(e2.getClass().getName(),
+ e2 instanceof IllegalArgumentException ||
+ e2 instanceof DatabaseException);
+ }
+ } else {
+ setValuePlusOne(iter, obj);
+ /* Ensure iterator position is correct. */
+ if (map.isOrdered()) {
+ assertTrue(iter.hasPrevious());
+ obj = iter.previous();
+ assertEquals(i, intIter(coll, obj));
+ assertTrue(iter.hasNext());
+ obj = iter.next();
+ assertEquals(i, intIter(coll, obj));
+ }
+ }
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ }
+
+ void setValuePlusOne(ListIterator iter, Object obj) {
+
+ if (obj instanceof Map.Entry) {
+ Map.Entry entry = (Map.Entry) obj;
+ Long key = (Long) entry.getKey();
+ Object oldVal = entry.getValue();
+ Object val = makeVal(key.intValue() + 1);
+ if (isEntityBinding) {
+ try {
+ // must fail on attempt to change the key via an entity
+ entry.setValue(val);
+ fail();
+ } catch (IllegalArgumentException e) {}
+ val = makeEntity(key.intValue(), key.intValue() + 1);
+ }
+ entry.setValue(val);
+ assertEquals(val, entry.getValue());
+ assertEquals(val, map.get(key));
+ assertTrue(map.duplicates(key).contains(val));
+ checkDupsSize(1, map.duplicates(key));
+ entry.setValue(oldVal);
+ assertEquals(oldVal, entry.getValue());
+ assertEquals(oldVal, map.get(key));
+ assertTrue(map.duplicates(key).contains(oldVal));
+ checkDupsSize(1, map.duplicates(key));
+ } else {
+ Object oldVal = obj;
+ Long key = makeKey(intVal(obj));
+ Object val = makeVal(key.intValue() + 1);
+ if (isEntityBinding) {
+ try {
+ // must fail on attempt to change the key via an entity
+ iter.set(val);
+ fail();
+ } catch (IllegalArgumentException e) {}
+ val = makeEntity(key.intValue(), key.intValue() + 1);
+ }
+ iter.set(val);
+ assertEquals(val, map.get(key));
+ assertTrue(map.duplicates(key).contains(val));
+ checkDupsSize(1, map.duplicates(key));
+ iter.set(oldVal);
+ assertEquals(oldVal, map.get(key));
+ assertTrue(map.duplicates(key).contains(oldVal));
+ checkDupsSize(1, map.duplicates(key));
+ }
+ }
+
+ void removeAll()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertTrue(!map.isEmpty());
+ ListIterator iter = null;
+ try {
+ if (list != null) {
+ iter = (ListIterator) iterator(list);
+ } else {
+ iter = (ListIterator) iterator(map.values());
+ }
+ iteratorSetAndRemoveNotAllowed(iter);
+
+ Object val = iter.next();
+ assertNotNull(val);
+ iter.remove();
+ iteratorSetAndRemoveNotAllowed(iter);
+
+ if (index == null) {
+ val = iter.next();
+ assertNotNull(val);
+ iter.set(val);
+
+ if (map.areDuplicatesAllowed()) {
+ iter.add(makeVal(intVal(val), intVal(val) + 1));
+ iteratorSetAndRemoveNotAllowed(iter);
+ }
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ map.clear();
+ assertTrue(map.isEmpty());
+ assertTrue(map.entrySet().isEmpty());
+ assertTrue(map.keySet().isEmpty());
+ assertTrue(map.values().isEmpty());
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(map.get(key));
+ assertTrue(!map.duplicates(key).contains(val));
+ checkDupsSize(0, map.duplicates(key));
+ }
+ }
+ });
+ }
+
+ void clearAll()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ map.clear();
+ assertTrue(map.isEmpty());
+ }
+ });
+ }
+
+ /**
+ * Tests that removing while iterating works properly, especially when
+ * removing everything in the key range or everything from some point to
+ * the end of the range. [#15858]
+ */
+ void removeIter()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ ListIterator iter;
+
+ /* Save contents. */
+ HashMap<Object,Object> savedMap =
+ new HashMap<Object,Object>(map);
+ assertEquals(savedMap, map);
+
+ /* Remove all moving forward. */
+ iter = (ListIterator) iterator(map.keySet());
+ try {
+ while (iter.hasNext()) {
+ assertNotNull(iter.next());
+ iter.remove();
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(!iter.hasPrevious());
+ assertTrue(map.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ /* Restore contents. */
+ imap.putAll(savedMap);
+ assertEquals(savedMap, map);
+
+ /* Remove all moving backward. */
+ iter = (ListIterator) iterator(map.keySet());
+ try {
+ while (iter.hasNext()) {
+ assertNotNull(iter.next());
+ }
+ while (iter.hasPrevious()) {
+ assertNotNull(iter.previous());
+ iter.remove();
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(!iter.hasPrevious());
+ assertTrue(map.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ /* Restore contents. */
+ imap.putAll(savedMap);
+ assertEquals(savedMap, map);
+
+ int first = Math.max(1, beginKey);
+ int last = Math.min(maxKey, endKey);
+
+ /* Skip N forward, remove all from that point forward. */
+ for (int readTo = first + 1; readTo <= last; readTo += 1) {
+ iter = (ListIterator) iterator(map.keySet());
+ try {
+ for (int i = first; i < readTo; i += 1) {
+ assertTrue(iter.hasNext());
+ assertNotNull(iter.next());
+ }
+ for (int i = readTo; i <= last; i += 1) {
+ assertTrue(iter.hasNext());
+ assertNotNull(iter.next());
+ iter.remove();
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(iter.hasPrevious());
+ assertEquals(readTo - first, map.size());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ /* Restore contents. */
+ for (Map.Entry entry : savedMap.entrySet()) {
+ if (!imap.entrySet().contains(entry)) {
+ imap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ assertEquals(savedMap, map);
+ }
+
+ /* Skip N backward, remove all from that point backward. */
+ for (int readTo = last - 1; readTo >= first; readTo -= 1) {
+ iter = (ListIterator) iterator(map.keySet());
+ try {
+ while (iter.hasNext()) {
+ assertNotNull(iter.next());
+ }
+ for (int i = last; i > readTo; i -= 1) {
+ assertTrue(iter.hasPrevious());
+ assertNotNull(iter.previous());
+ }
+ for (int i = readTo; i >= first; i -= 1) {
+ assertTrue(iter.hasPrevious());
+ assertNotNull(iter.previous());
+ iter.remove();
+ }
+ assertTrue(!iter.hasPrevious());
+ assertTrue(iter.hasNext());
+ assertEquals(last - readTo, map.size());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ /* Restore contents. */
+ for (Map.Entry entry : savedMap.entrySet()) {
+ if (!imap.entrySet().contains(entry)) {
+ imap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ assertEquals(savedMap, map);
+ }
+ }
+ });
+ }
+
+ void iteratorSetAndRemoveNotAllowed(ListIterator i) {
+
+ try {
+ i.remove();
+ fail();
+ } catch (IllegalStateException e) {}
+
+ if (index == null) {
+ try {
+ Object val = makeVal(1);
+ i.set(val);
+ fail();
+ } catch (IllegalStateException e) {}
+ }
+ }
+
+ void removeOdd()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ boolean toggle = false;
+ for (int i = beginKey; i <= endKey; i += 2) {
+ toggle = !toggle;
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ if (toggle) {
+ assertTrue(map.keySet().contains(key));
+ assertTrue(map.keySet().remove(key));
+ assertTrue(!map.keySet().contains(key));
+ } else {
+ assertTrue(map.containsValue(val));
+ Object oldVal = map.remove(key);
+ assertEquals(oldVal, val);
+ assertTrue(!map.containsKey(key));
+ assertTrue(!map.containsValue(val));
+ }
+ assertNull(map.get(key));
+ assertTrue(!map.duplicates(key).contains(val));
+ checkDupsSize(0, map.duplicates(key));
+ }
+ }
+ });
+ }
+
+ void removeOddEntity()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertTrue(map.values().contains(val));
+ assertTrue(map.values().remove(val));
+ assertTrue(!map.values().contains(val));
+ assertNull(map.get(key));
+ assertTrue(!map.duplicates(key).contains(val));
+ checkDupsSize(0, map.duplicates(key));
+ }
+ }
+ });
+ }
+
+ void removeOddEntry()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = mapEntry(i);
+ assertTrue(map.entrySet().contains(val));
+ assertTrue(map.entrySet().remove(val));
+ assertTrue(!map.entrySet().contains(val));
+ assertNull(map.get(key));
+ }
+ }
+ });
+ }
+
+ void removeOddIter()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ Iterator iter = iterator(map.keySet());
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Long key = (Long) iter.next();
+ assertNotNull(key);
+ if (map instanceof SortedMap) {
+ assertEquals(makeKey(i), key);
+ }
+ if ((key.intValue() & 1) != 0) {
+ iter.remove();
+ }
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ }
+
+ void removeOddList()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ // remove by index
+ // (with entity binding, embbeded keys in values are
+ // being changed so we can't use values for comparison)
+ int idx = (i - beginKey) / 2;
+ Object val = makeVal(i);
+ if (!isEntityBinding) {
+ assertTrue(list.contains(val));
+ assertEquals(val, list.get(idx));
+ assertEquals(idx, list.indexOf(val));
+ }
+ assertNotNull(list.get(idx));
+ if (isEntityBinding) {
+ assertNotNull(list.remove(idx));
+ } else {
+ assertTrue(list.contains(val));
+ assertEquals(val, list.remove(idx));
+ }
+ assertTrue(!list.remove(val));
+ assertTrue(!list.contains(val));
+ assertTrue(!val.equals(list.get(idx)));
+ }
+ }
+ });
+ }
+
+ void removeOddListValue()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ // for non-entity case remove by value
+ // (with entity binding, embbeded keys in values are
+ // being changed so we can't use values for comparison)
+ int idx = (i - beginKey) / 2;
+ Object val = makeVal(i);
+ assertTrue(list.contains(val));
+ assertEquals(val, list.get(idx));
+ assertEquals(idx, list.indexOf(val));
+ assertTrue(list.remove(val));
+ assertTrue(!list.remove(val));
+ assertTrue(!list.contains(val));
+ assertTrue(!val.equals(list.get(idx)));
+ }
+ }
+ });
+ }
+
+ void addOdd()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ // add using Map.put()
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ assertNull(imap.put(key, val));
+ assertEquals(val, imap.get(key));
+ assertTrue(imap.duplicates(key).contains(val));
+ checkDupsSize(1, imap.duplicates(key));
+ if (isEntityBinding) {
+ assertTrue(!imap.values().add(val));
+ }
+ if (!imap.areDuplicatesAllowed()) {
+ assertEquals(val, imap.put(key, val));
+ }
+ }
+ }
+ });
+ }
+
+ void addOddEntity()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ // add using Map.values().add()
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ assertTrue(!imap.values().contains(val));
+ assertTrue(imap.values().add(val));
+ assertEquals(val, imap.get(key));
+ assertTrue(imap.values().contains(val));
+ assertTrue(imap.duplicates(key).contains(val));
+ checkDupsSize(1, imap.duplicates(key));
+ if (isEntityBinding) {
+ assertTrue(!imap.values().add(val));
+ }
+ }
+ }
+ });
+ }
+
+ void addOddDup()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ // add using Map.duplicates().add()
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ assertTrue(!imap.values().contains(val));
+ assertTrue(imap.duplicates(key).add(val));
+ assertEquals(val, imap.get(key));
+ assertTrue(imap.values().contains(val));
+ assertTrue(imap.duplicates(key).contains(val));
+ checkDupsSize(1, imap.duplicates(key));
+ assertTrue(!imap.duplicates(key).add(val));
+ if (isEntityBinding) {
+ assertTrue(!imap.values().add(val));
+ }
+ }
+ }
+ });
+ }
+
+ void addOddList()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ int idx = i - beginKey;
+ Object val = makeVal(i);
+ assertTrue(!list.contains(val));
+ assertTrue(!val.equals(list.get(idx)));
+ list.add(idx, val);
+ assertTrue(list.contains(val));
+ assertEquals(val, list.get(idx));
+ }
+ }
+ });
+ }
+
+ void addAllList()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ Object val = makeVal(i);
+ assertTrue(!list.contains(val));
+ assertTrue(list.add(val));
+ assertTrue(list.contains(val));
+ assertEquals(val, list.get(idx));
+ }
+ }
+ });
+ }
+
+ void removeAllList()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertTrue(!list.isEmpty());
+ list.clear();
+ assertTrue(list.isEmpty());
+ for (int i = beginKey; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ assertNull(list.get(idx));
+ }
+ }
+ });
+ }
+
+ /**
+ * Tests ConcurentMap methods implemented by StordMap. Starts with an
+ * empty DB and ends with an empty DB. [#16218]
+ */
+ void testConcurrentMap()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ Object valPlusOne = makeVal(i, i + 1);
+ assertFalse(imap.containsKey(key));
+
+ assertNull(imap.putIfAbsent(key, val));
+ assertEquals(val, imap.get(key));
+
+ assertEquals(val, imap.putIfAbsent(key, val));
+ assertEquals(val, imap.get(key));
+
+ if (!imap.areDuplicatesAllowed()) {
+ assertEquals(val, imap.replace(key, valPlusOne));
+ assertEquals(valPlusOne, imap.get(key));
+
+ assertEquals(valPlusOne, imap.replace(key, val));
+ assertEquals(val, imap.get(key));
+
+ assertFalse(imap.replace(key, valPlusOne, val));
+ assertEquals(val, imap.get(key));
+
+ assertTrue(imap.replace(key, val, valPlusOne));
+ assertEquals(valPlusOne, imap.get(key));
+
+ assertTrue(imap.replace(key, valPlusOne, val));
+ assertEquals(val, imap.get(key));
+ }
+
+ assertFalse(imap.remove(key, valPlusOne));
+ assertTrue(imap.containsKey(key));
+
+ assertTrue(imap.remove(key, val));
+ assertFalse(imap.containsKey(key));
+
+ assertNull(imap.replace(key, val));
+ assertFalse(imap.containsKey(key));
+ }
+ }
+ });
+ }
+
+ void testIterAddList()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ ListIterator i = (ListIterator) iterator(list);
+ try {
+ assertTrue(!i.hasNext());
+ i.add(makeVal(3));
+ assertTrue(!i.hasNext());
+ assertTrue(i.hasPrevious());
+ assertEquals(3, intVal(i.previous()));
+
+ i.add(makeVal(1));
+ assertTrue(i.hasPrevious());
+ assertTrue(i.hasNext());
+ assertEquals(1, intVal(i.previous()));
+ assertTrue(i.hasNext());
+ assertEquals(1, intVal(i.next()));
+ assertTrue(i.hasNext());
+ assertEquals(3, intVal(i.next()));
+ assertEquals(3, intVal(i.previous()));
+
+ assertTrue(i.hasNext());
+ i.add(makeVal(2));
+ assertTrue(i.hasNext());
+ assertTrue(i.hasPrevious());
+ assertEquals(2, intVal(i.previous()));
+ assertTrue(i.hasNext());
+ assertEquals(2, intVal(i.next()));
+ assertTrue(i.hasNext());
+ assertEquals(3, intVal(i.next()));
+
+ assertTrue(!i.hasNext());
+ i.add(makeVal(4));
+ i.add(makeVal(5));
+ assertTrue(!i.hasNext());
+ assertEquals(5, intVal(i.previous()));
+ assertEquals(4, intVal(i.previous()));
+ assertEquals(3, intVal(i.previous()));
+ assertEquals(2, intVal(i.previous()));
+ assertEquals(1, intVal(i.previous()));
+ assertTrue(!i.hasPrevious());
+ } finally {
+ StoredIterator.close(i);
+ }
+ }
+ });
+ }
+
+ void testIterAddDuplicates()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertNull(imap.put(makeKey(1), makeVal(1)));
+ ListIterator i =
+ (ListIterator) iterator(imap.duplicates(makeKey(1)));
+ try {
+ if (imap.areDuplicatesOrdered()) {
+ i.add(makeVal(1, 4));
+ i.add(makeVal(1, 2));
+ i.add(makeVal(1, 3));
+ while (i.hasPrevious()) i.previous();
+ assertEquals(1, intVal(i.next()));
+ assertEquals(2, intVal(i.next()));
+ assertEquals(3, intVal(i.next()));
+ assertEquals(4, intVal(i.next()));
+ assertTrue(!i.hasNext());
+ } else {
+ assertEquals(1, intVal(i.next()));
+ i.add(makeVal(1, 2));
+ i.add(makeVal(1, 3));
+ assertTrue(!i.hasNext());
+ assertTrue(i.hasPrevious());
+ assertEquals(3, intVal(i.previous()));
+ assertEquals(2, intVal(i.previous()));
+ assertEquals(1, intVal(i.previous()));
+ assertTrue(!i.hasPrevious());
+ i.add(makeVal(1, 4));
+ i.add(makeVal(1, 5));
+ assertTrue(i.hasNext());
+ assertEquals(5, intVal(i.previous()));
+ assertEquals(4, intVal(i.previous()));
+ assertTrue(!i.hasPrevious());
+ assertEquals(4, intVal(i.next()));
+ assertEquals(5, intVal(i.next()));
+ assertEquals(1, intVal(i.next()));
+ assertEquals(2, intVal(i.next()));
+ assertEquals(3, intVal(i.next()));
+ assertTrue(!i.hasNext());
+ }
+ } finally {
+ StoredIterator.close(i);
+ }
+ }
+ });
+ }
+
+ void readAll()
+ throws Exception {
+
+ readRunner.run(new TransactionWorker() {
+ public void doWork() {
+ // map
+
+ assertNotNull(map.toString());
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = map.get(key);
+ assertEquals(makeVal(i), val);
+ assertTrue(map.containsKey(key));
+ assertTrue(map.containsValue(val));
+ assertTrue(map.keySet().contains(key));
+ assertTrue(map.values().contains(val));
+ assertTrue(map.duplicates(key).contains(val));
+ checkDupsSize(1, map.duplicates(key));
+ }
+ assertNull(map.get(makeKey(-1)));
+ assertNull(map.get(makeKey(0)));
+ assertNull(map.get(makeKey(beginKey - 1)));
+ assertNull(map.get(makeKey(endKey + 1)));
+ checkDupsSize(0, map.duplicates(makeKey(-1)));
+ checkDupsSize(0, map.duplicates(makeKey(0)));
+ checkDupsSize(0, map.duplicates(makeKey(beginKey - 1)));
+ checkDupsSize(0, map.duplicates(makeKey(endKey + 1)));
+
+ // entrySet
+
+ Set set = map.entrySet();
+ assertNotNull(set.toString());
+ assertEquals(beginKey > endKey, set.isEmpty());
+ Iterator iter = iterator(set);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Map.Entry entry = (Map.Entry) iter.next();
+ Long key = (Long) entry.getKey();
+ Object val = entry.getValue();
+ if (map instanceof SortedMap) {
+ assertEquals(intKey(key), i);
+ }
+ assertEquals(intKey(key), intVal(val));
+ assertTrue(set.contains(entry));
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ Map.Entry[] entries =
+ (Map.Entry[]) set.toArray(new Map.Entry[0]);
+ assertNotNull(entries);
+ assertEquals(endKey - beginKey + 1, entries.length);
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Map.Entry entry = entries[i - beginKey];
+ assertNotNull(entry);
+ if (map instanceof SortedMap) {
+ assertEquals(makeKey(i), entry.getKey());
+ assertEquals(makeVal(i), entry.getValue());
+ }
+ }
+ readIterator(set, iterator(set), beginKey, endKey);
+ if (smap != null) {
+ SortedSet sset = (SortedSet) set;
+ if (beginKey == 1 && endKey >= 1) {
+ readIterator(sset,
+ iterator(sset.subSet(mapEntry(1),
+ mapEntry(2))),
+ 1, 1);
+ }
+ if (beginKey <= 2 && endKey >= 2) {
+ readIterator(sset,
+ iterator(sset.subSet(mapEntry(2),
+ mapEntry(3))),
+ 2, 2);
+ }
+ if (beginKey <= endKey) {
+ readIterator(sset,
+ iterator(sset.subSet
+ (mapEntry(endKey),
+ mapEntry(endKey + 1))),
+ endKey, endKey);
+ }
+ if (isSubMap()) {
+ if (beginKey <= endKey) {
+ if (rangeType != TAIL) {
+ try {
+ sset.subSet(mapEntry(endKey + 1),
+ mapEntry(endKey + 2));
+ fail();
+ } catch (IllegalArgumentException e) {}
+ }
+ if (rangeType != HEAD) {
+ try {
+ sset.subSet(mapEntry(0),
+ mapEntry(1));
+ fail();
+ } catch (IllegalArgumentException e) {}
+ }
+ }
+ } else {
+ readIterator(sset,
+ iterator(sset.subSet
+ (mapEntry(endKey + 1),
+ mapEntry(endKey + 2))),
+ endKey, endKey - 1);
+ readIterator(sset,
+ iterator(sset.subSet(mapEntry(0),
+ mapEntry(1))),
+ 0, -1);
+ }
+ }
+
+ // keySet
+
+ set = map.keySet();
+ assertNotNull(set.toString());
+ assertEquals(beginKey > endKey, set.isEmpty());
+ iter = iterator(set);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Long key = (Long) iter.next();
+ assertTrue(set.contains(key));
+ Object val = map.get(key);
+ if (map instanceof SortedMap) {
+ assertEquals(key, makeKey(i));
+ }
+ assertEquals(intKey(key), intVal(val));
+ }
+ assertTrue("" + beginKey + ' ' + endKey, !iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ Long[] keys = (Long[]) set.toArray(new Long[0]);
+ assertNotNull(keys);
+ assertEquals(endKey - beginKey + 1, keys.length);
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = keys[i - beginKey];
+ assertNotNull(key);
+ if (map instanceof SortedMap) {
+ assertEquals(makeKey(i), key);
+ }
+ }
+ readIterator(set, iterator(set), beginKey, endKey);
+
+ // values
+
+ Collection coll = map.values();
+ assertNotNull(coll.toString());
+ assertEquals(beginKey > endKey, coll.isEmpty());
+ iter = iterator(coll);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Object val = iter.next();
+ if (map instanceof SortedMap) {
+ assertEquals(makeVal(i), val);
+ }
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ Object[] values = coll.toArray();
+ assertNotNull(values);
+ assertEquals(endKey - beginKey + 1, values.length);
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Object val = values[i - beginKey];
+ assertNotNull(val);
+ if (map instanceof SortedMap) {
+ assertEquals(makeVal(i), val);
+ }
+ }
+ readIterator(coll, iterator(coll), beginKey, endKey);
+
+ // list
+
+ if (list != null) {
+ assertNotNull(list.toString());
+ assertEquals(beginKey > endKey, list.isEmpty());
+ for (int i = beginKey; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ Object val = list.get(idx);
+ assertEquals(makeVal(i), val);
+ assertTrue(list.contains(val));
+ assertEquals(idx, list.indexOf(val));
+ assertEquals(idx, list.lastIndexOf(val));
+ }
+ ListIterator li = (ListIterator) iterator(list);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ assertTrue(li.hasNext());
+ assertEquals(idx, li.nextIndex());
+ Object val = li.next();
+ assertEquals(makeVal(i), val);
+ assertEquals(idx, li.previousIndex());
+ }
+ assertTrue(!li.hasNext());
+ } finally {
+ StoredIterator.close(li);
+ }
+ if (beginKey < endKey) {
+ li = list.listIterator(1);
+ try {
+ for (int i = beginKey + 1; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ assertTrue(li.hasNext());
+ assertEquals(idx, li.nextIndex());
+ Object val = li.next();
+ assertEquals(makeVal(i), val);
+ assertEquals(idx, li.previousIndex());
+ }
+ assertTrue(!li.hasNext());
+ } finally {
+ StoredIterator.close(li);
+ }
+ }
+ values = list.toArray();
+ assertNotNull(values);
+ assertEquals(endKey - beginKey + 1, values.length);
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Object val = values[i - beginKey];
+ assertNotNull(val);
+ assertEquals(makeVal(i), val);
+ }
+ readIterator(list, iterator(list), beginKey, endKey);
+ }
+
+ // first/last
+
+ if (smap != null) {
+ if (beginKey <= endKey &&
+ beginKey >= 1 && beginKey <= maxKey) {
+ assertEquals(makeKey(beginKey),
+ smap.firstKey());
+ assertEquals(makeKey(beginKey),
+ ((SortedSet) smap.keySet()).first());
+ Object entry = ((SortedSet) smap.entrySet()).first();
+ assertEquals(makeKey(beginKey),
+ ((Map.Entry) entry).getKey());
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(makeVal(beginKey),
+ ((SortedSet) smap.values()).first());
+ }
+ } else {
+ assertNull(smap.firstKey());
+ assertNull(((SortedSet) smap.keySet()).first());
+ assertNull(((SortedSet) smap.entrySet()).first());
+ if (smap.values() instanceof SortedSet) {
+ assertNull(((SortedSet) smap.values()).first());
+ }
+ }
+ if (beginKey <= endKey &&
+ endKey >= 1 && endKey <= maxKey) {
+ assertEquals(makeKey(endKey),
+ smap.lastKey());
+ assertEquals(makeKey(endKey),
+ ((SortedSet) smap.keySet()).last());
+ Object entry = ((SortedSet) smap.entrySet()).last();
+ assertEquals(makeKey(endKey),
+ ((Map.Entry) entry).getKey());
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(makeVal(endKey),
+ ((SortedSet) smap.values()).last());
+ }
+ } else {
+ assertNull(smap.lastKey());
+ assertNull(((SortedSet) smap.keySet()).last());
+ assertNull(((SortedSet) smap.entrySet()).last());
+ if (smap.values() instanceof SortedSet) {
+ assertNull(((SortedSet) smap.values()).last());
+ }
+ }
+ }
+ }
+ });
+ }
+
+ void readEven()
+ throws Exception {
+
+ readRunner.run(new TransactionWorker() {
+ public void doWork() {
+ int readBegin = ((beginKey & 1) != 0) ?
+ (beginKey + 1) : beginKey;
+ int readEnd = ((endKey & 1) != 0) ? (endKey - 1) : endKey;
+ int readIncr = 2;
+
+ // map
+
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ if ((i & 1) == 0) {
+ Object val = map.get(key);
+ assertEquals(makeVal(i), val);
+ assertTrue(map.containsKey(key));
+ assertTrue(map.containsValue(val));
+ assertTrue(map.keySet().contains(key));
+ assertTrue(map.values().contains(val));
+ assertTrue(map.duplicates(key).contains(val));
+ checkDupsSize(1, map.duplicates(key));
+ } else {
+ Object val = makeVal(i);
+ assertTrue(!map.containsKey(key));
+ assertTrue(!map.containsValue(val));
+ assertTrue(!map.keySet().contains(key));
+ assertTrue(!map.values().contains(val));
+ assertTrue(!map.duplicates(key).contains(val));
+ checkDupsSize(0, map.duplicates(key));
+ }
+ }
+
+ // entrySet
+
+ Set set = map.entrySet();
+ assertEquals(beginKey > endKey, set.isEmpty());
+ Iterator iter = iterator(set);
+ try {
+ for (int i = readBegin; i <= readEnd; i += readIncr) {
+ assertTrue(iter.hasNext());
+ Map.Entry entry = (Map.Entry) iter.next();
+ Long key = (Long) entry.getKey();
+ Object val = entry.getValue();
+ if (map instanceof SortedMap) {
+ assertEquals(intKey(key), i);
+ }
+ assertEquals(intKey(key), intVal(val));
+ assertTrue(set.contains(entry));
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ // keySet
+
+ set = map.keySet();
+ assertEquals(beginKey > endKey, set.isEmpty());
+ iter = iterator(set);
+ try {
+ for (int i = readBegin; i <= readEnd; i += readIncr) {
+ assertTrue(iter.hasNext());
+ Long key = (Long) iter.next();
+ assertTrue(set.contains(key));
+ Object val = map.get(key);
+ if (map instanceof SortedMap) {
+ assertEquals(key, makeKey(i));
+ }
+ assertEquals(intKey(key), intVal(val));
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ // values
+
+ Collection coll = map.values();
+ assertEquals(beginKey > endKey, coll.isEmpty());
+ iter = iterator(coll);
+ try {
+ for (int i = readBegin; i <= readEnd; i += readIncr) {
+ assertTrue(iter.hasNext());
+ Object val = iter.next();
+ if (map instanceof SortedMap) {
+ assertEquals(makeVal(i), val);
+ }
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ // list not used since keys may not be renumbered for this
+ // method to work in general
+
+ // first/last
+
+ if (smap != null) {
+ if (readBegin <= readEnd &&
+ readBegin >= 1 && readBegin <= maxKey) {
+ assertEquals(makeKey(readBegin),
+ smap.firstKey());
+ assertEquals(makeKey(readBegin),
+ ((SortedSet) smap.keySet()).first());
+ Object entry = ((SortedSet) smap.entrySet()).first();
+ assertEquals(makeKey(readBegin),
+ ((Map.Entry) entry).getKey());
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(makeVal(readBegin),
+ ((SortedSet) smap.values()).first());
+ }
+ } else {
+ assertNull(smap.firstKey());
+ assertNull(((SortedSet) smap.keySet()).first());
+ assertNull(((SortedSet) smap.entrySet()).first());
+ if (smap.values() instanceof SortedSet) {
+ assertNull(((SortedSet) smap.values()).first());
+ }
+ }
+ if (readBegin <= readEnd &&
+ readEnd >= 1 && readEnd <= maxKey) {
+ assertEquals(makeKey(readEnd),
+ smap.lastKey());
+ assertEquals(makeKey(readEnd),
+ ((SortedSet) smap.keySet()).last());
+ Object entry = ((SortedSet) smap.entrySet()).last();
+ assertEquals(makeKey(readEnd),
+ ((Map.Entry) entry).getKey());
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(makeVal(readEnd),
+ ((SortedSet) smap.values()).last());
+ }
+ } else {
+ assertNull(smap.lastKey());
+ assertNull(((SortedSet) smap.keySet()).last());
+ assertNull(((SortedSet) smap.entrySet()).last());
+ if (smap.values() instanceof SortedSet) {
+ assertNull(((SortedSet) smap.values()).last());
+ }
+ }
+ }
+ }
+ });
+ }
+
+ void readEvenList()
+ throws Exception {
+
+ readRunner.run(new TransactionWorker() {
+ public void doWork() {
+ int readBegin = ((beginKey & 1) != 0) ?
+ (beginKey + 1) : beginKey;
+ int readEnd = ((endKey & 1) != 0) ? (endKey - 1) : endKey;
+ int readIncr = 2;
+
+ assertEquals(beginKey > endKey, list.isEmpty());
+ ListIterator iter = (ListIterator) iterator(list);
+ try {
+ int idx = 0;
+ for (int i = readBegin; i <= readEnd; i += readIncr) {
+ assertTrue(iter.hasNext());
+ assertEquals(idx, iter.nextIndex());
+ Object val = iter.next();
+ assertEquals(idx, iter.previousIndex());
+ if (isEntityBinding) {
+ assertEquals(i, intVal(val));
+ } else {
+ assertEquals(makeVal(i), val);
+ }
+ idx += 1;
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ }
+
+ void readIterator(Collection coll, Iterator iter,
+ int beginValue, int endValue) {
+
+ ListIterator li = (ListIterator) iter;
+ boolean isList = (coll instanceof List);
+ Iterator clone = null;
+ try {
+ // at beginning
+ assertTrue(!li.hasPrevious());
+ assertTrue(!li.hasPrevious());
+ try { li.previous(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(-1, li.previousIndex());
+ }
+ if (endValue < beginValue) {
+ // is empty
+ assertTrue(!iter.hasNext());
+ try { iter.next(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(Integer.MAX_VALUE, li.nextIndex());
+ }
+ }
+ // loop thru all and collect in array
+ int[] values = new int[endValue - beginValue + 1];
+ for (int i = beginValue; i <= endValue; i += 1) {
+ assertTrue(iter.hasNext());
+ int idx = i - beginKey;
+ if (isList) {
+ assertEquals(idx, li.nextIndex());
+ }
+ int value = intIter(coll, iter.next());
+ if (isList) {
+ assertEquals(idx, li.previousIndex());
+ }
+ values[i - beginValue] = value;
+ if (((StoredCollection) coll).isOrdered()) {
+ assertEquals(i, value);
+ } else {
+ assertTrue(value >= beginValue);
+ assertTrue(value <= endValue);
+ }
+ }
+ // at end
+ assertTrue(!iter.hasNext());
+ try { iter.next(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(Integer.MAX_VALUE, li.nextIndex());
+ }
+ // clone at same position
+ clone = StoredCollections.iterator(iter);
+ assertTrue(!clone.hasNext());
+ // loop thru in reverse
+ for (int i = endValue; i >= beginValue; i -= 1) {
+ assertTrue(li.hasPrevious());
+ int idx = i - beginKey;
+ if (isList) {
+ assertEquals(idx, li.previousIndex());
+ }
+ int value = intIter(coll, li.previous());
+ if (isList) {
+ assertEquals(idx, li.nextIndex());
+ }
+ assertEquals(values[i - beginValue], value);
+ }
+ // clone should not have changed
+ assertTrue(!clone.hasNext());
+ // at beginning
+ assertTrue(!li.hasPrevious());
+ try { li.previous(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(-1, li.previousIndex());
+ }
+ // loop thru with some back-and-forth
+ for (int i = beginValue; i <= endValue; i += 1) {
+ assertTrue(iter.hasNext());
+ int idx = i - beginKey;
+ if (isList) {
+ assertEquals(idx, li.nextIndex());
+ }
+ Object obj = iter.next();
+ if (isList) {
+ assertEquals(idx, li.previousIndex());
+ }
+ assertEquals(obj, li.previous());
+ if (isList) {
+ assertEquals(idx, li.nextIndex());
+ }
+ assertEquals(obj, iter.next());
+ if (isList) {
+ assertEquals(idx, li.previousIndex());
+ }
+ int value = intIter(coll, obj);
+ assertEquals(values[i - beginValue], value);
+ }
+ // at end
+ assertTrue(!iter.hasNext());
+ try { iter.next(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(Integer.MAX_VALUE, li.nextIndex());
+ }
+ } finally {
+ StoredIterator.close(iter);
+ StoredIterator.close(clone);
+ }
+ }
+
+ void bulkOperations()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ HashMap hmap = new HashMap();
+ for (int i = Math.max(1, beginKey);
+ i <= Math.min(maxKey, endKey);
+ i += 1) {
+ hmap.put(makeKey(i), makeVal(i));
+ }
+ assertEquals(hmap, map);
+ assertEquals(hmap.entrySet(), map.entrySet());
+ assertEquals(hmap.keySet(), map.keySet());
+ assertEquals(map.values(), hmap.values());
+
+ assertTrue(map.entrySet().containsAll(hmap.entrySet()));
+ assertTrue(map.keySet().containsAll(hmap.keySet()));
+ assertTrue(map.values().containsAll(hmap.values()));
+
+ map.clear();
+ assertTrue(map.isEmpty());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+
+ assertTrue(map.entrySet().removeAll(hmap.entrySet()));
+ assertTrue(map.entrySet().isEmpty());
+ assertTrue(!map.entrySet().removeAll(hmap.entrySet()));
+ assertTrue(imap.entrySet().addAll(hmap.entrySet()));
+ assertTrue(map.entrySet().containsAll(hmap.entrySet()));
+ assertTrue(!imap.entrySet().addAll(hmap.entrySet()));
+ assertEquals(hmap, map);
+
+ assertTrue(!map.entrySet().retainAll(hmap.entrySet()));
+ assertEquals(hmap, map);
+ assertTrue(map.entrySet().retainAll(Collections.EMPTY_SET));
+ assertTrue(map.isEmpty());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+
+ assertTrue(map.values().removeAll(hmap.values()));
+ assertTrue(map.values().isEmpty());
+ assertTrue(!map.values().removeAll(hmap.values()));
+ if (isEntityBinding) {
+ assertTrue(imap.values().addAll(hmap.values()));
+ assertTrue(map.values().containsAll(hmap.values()));
+ assertTrue(!imap.values().addAll(hmap.values()));
+ } else {
+ imap.putAll(hmap);
+ }
+ assertEquals(hmap, map);
+
+ assertTrue(!map.values().retainAll(hmap.values()));
+ assertEquals(hmap, map);
+ assertTrue(map.values().retainAll(Collections.EMPTY_SET));
+ assertTrue(map.isEmpty());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+
+ assertTrue(map.keySet().removeAll(hmap.keySet()));
+ assertTrue(map.keySet().isEmpty());
+ assertTrue(!map.keySet().removeAll(hmap.keySet()));
+ assertTrue(imap.keySet().addAll(hmap.keySet()));
+ assertTrue(imap.keySet().containsAll(hmap.keySet()));
+ if (index != null) {
+ assertTrue(map.keySet().isEmpty());
+ }
+ assertTrue(!imap.keySet().addAll(hmap.keySet()));
+ // restore values to non-null
+ imap.keySet().removeAll(hmap.keySet());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+
+ assertTrue(!map.keySet().retainAll(hmap.keySet()));
+ assertEquals(hmap, map);
+ assertTrue(map.keySet().retainAll(Collections.EMPTY_SET));
+ assertTrue(map.isEmpty());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+ }
+ });
+ }
+
+ void bulkListOperations()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ ArrayList alist = new ArrayList();
+ for (int i = beginKey; i <= endKey; i += 1) {
+ alist.add(makeVal(i));
+ }
+
+ assertEquals(alist, list);
+ assertTrue(list.containsAll(alist));
+
+ if (isListAddAllowed()) {
+ list.clear();
+ assertTrue(list.isEmpty());
+ assertTrue(ilist.addAll(alist));
+ assertEquals(alist, list);
+ }
+
+ assertTrue(!list.retainAll(alist));
+ assertEquals(alist, list);
+
+ if (isListAddAllowed()) {
+ assertTrue(list.retainAll(Collections.EMPTY_SET));
+ assertTrue(list.isEmpty());
+ assertTrue(ilist.addAll(alist));
+ assertEquals(alist, list);
+ }
+
+ if (isListAddAllowed() && !isEntityBinding) {
+ // deleting in a renumbered list with entity binding will
+ // change the values dynamically, making it very difficult
+ // to test
+ assertTrue(list.removeAll(alist));
+ assertTrue(list.isEmpty());
+ assertTrue(!list.removeAll(alist));
+ assertTrue(ilist.addAll(alist));
+ assertTrue(list.containsAll(alist));
+ assertEquals(alist, list);
+ }
+
+ if (isListAddAllowed() && !isEntityBinding) {
+ // addAll at an index is also very difficult to test with
+ // an entity binding
+
+ // addAll at first index
+ ilist.addAll(beginKey, alist);
+ assertTrue(list.containsAll(alist));
+ assertEquals(2 * alist.size(), countElements(list));
+ for (int i = beginKey; i <= endKey; i += 1)
+ ilist.remove(beginKey);
+ assertEquals(alist, list);
+
+ // addAll at last index
+ ilist.addAll(endKey, alist);
+ assertTrue(list.containsAll(alist));
+ assertEquals(2 * alist.size(), countElements(list));
+ for (int i = beginKey; i <= endKey; i += 1)
+ ilist.remove(endKey);
+ assertEquals(alist, list);
+
+ // addAll in the middle
+ ilist.addAll(endKey - 1, alist);
+ assertTrue(list.containsAll(alist));
+ assertEquals(2 * alist.size(), countElements(list));
+ for (int i = beginKey; i <= endKey; i += 1)
+ ilist.remove(endKey - 1);
+ assertEquals(alist, list);
+ }
+ }
+ });
+ }
+
+ void readWriteRange(final int type, final int rangeBegin,
+ final int rangeEnd)
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ setRange(type, rangeBegin, rangeEnd);
+ createOutOfRange(rangeBegin, rangeEnd);
+ if (rangeType != TAIL) {
+ writeOutOfRange(new Long(rangeEnd + 1));
+ }
+ if (rangeType != HEAD) {
+ writeOutOfRange(new Long(rangeBegin - 1));
+ }
+ if (rangeBegin <= rangeEnd) {
+ updateAll();
+ }
+ if (rangeBegin < rangeEnd && !map.areKeysRenumbered()) {
+ bulkOperations();
+ removeIter();
+ }
+ readAll();
+ clearRange();
+ }
+ });
+ }
+
+ void setRange(int type, int rangeBegin, int rangeEnd) {
+
+ rangeType = type;
+ saveMap = map;
+ saveSMap = smap;
+ saveList = list;
+ int listBegin = rangeBegin - beginKey;
+ boolean canMakeSubList = (list != null && listBegin>= 0);
+ if (!canMakeSubList) {
+ list = null;
+ }
+ if (list != null) {
+ try {
+ list.subList(-1, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ }
+ switch (type) {
+
+ case SUB:
+ smap = (StoredSortedMap) smap.subMap(makeKey(rangeBegin),
+ makeKey(rangeEnd + 1));
+ if (canMakeSubList) {
+ list = (StoredList) list.subList(listBegin,
+ rangeEnd + 1 - beginKey);
+ }
+ // check for equivalent ranges
+ assertEquals(smap,
+ (saveSMap).subMap(
+ makeKey(rangeBegin), true,
+ makeKey(rangeEnd + 1), false));
+ assertEquals(smap.entrySet(),
+ ((StoredSortedEntrySet) saveSMap.entrySet()).subSet(
+ mapEntry(rangeBegin), true,
+ mapEntry(rangeEnd + 1), false));
+ assertEquals(smap.keySet(),
+ ((StoredSortedKeySet) saveSMap.keySet()).subSet(
+ makeKey(rangeBegin), true,
+ makeKey(rangeEnd + 1), false));
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(smap.values(),
+ ((StoredSortedValueSet) saveSMap.values()).subSet(
+ makeVal(rangeBegin), true,
+ makeVal(rangeEnd + 1), false));
+ }
+ break;
+ case HEAD:
+ smap = (StoredSortedMap) smap.headMap(makeKey(rangeEnd + 1));
+ if (canMakeSubList) {
+ list = (StoredList) list.subList(0,
+ rangeEnd + 1 - beginKey);
+ }
+ // check for equivalent ranges
+ assertEquals(smap,
+ (saveSMap).headMap(
+ makeKey(rangeEnd + 1), false));
+ assertEquals(smap.entrySet(),
+ ((StoredSortedEntrySet) saveSMap.entrySet()).headSet(
+ mapEntry(rangeEnd + 1), false));
+ assertEquals(smap.keySet(),
+ ((StoredSortedKeySet) saveSMap.keySet()).headSet(
+ makeKey(rangeEnd + 1), false));
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(smap.values(),
+ ((StoredSortedValueSet) saveSMap.values()).headSet(
+ makeVal(rangeEnd + 1), false));
+ }
+ break;
+ case TAIL:
+ smap = (StoredSortedMap) smap.tailMap(makeKey(rangeBegin));
+ if (canMakeSubList) {
+ list = (StoredList) list.subList(listBegin,
+ maxKey + 1 - beginKey);
+ }
+ // check for equivalent ranges
+ assertEquals(smap,
+ (saveSMap).tailMap(
+ makeKey(rangeBegin), true));
+ assertEquals(smap.entrySet(),
+ ((StoredSortedEntrySet) saveSMap.entrySet()).tailSet(
+ mapEntry(rangeBegin), true));
+ assertEquals(smap.keySet(),
+ ((StoredSortedKeySet) saveSMap.keySet()).tailSet(
+ makeKey(rangeBegin), true));
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(smap.values(),
+ ((StoredSortedValueSet) saveSMap.values()).tailSet(
+ makeVal(rangeBegin), true));
+ }
+ break;
+ default: throw new RuntimeException();
+ }
+ map = smap;
+ beginKey = rangeBegin;
+ if (rangeBegin < 1 || rangeEnd > maxKey) {
+ endKey = rangeBegin - 1; // force empty range for readAll()
+ } else {
+ endKey = rangeEnd;
+ }
+ }
+
+ void clearRange() {
+
+ rangeType = NONE;
+ beginKey = 1;
+ endKey = maxKey;
+ map = saveMap;
+ smap = saveSMap;
+ list = saveList;
+ }
+
+ void createOutOfRange(int rangeBegin, int rangeEnd) {
+ // map
+
+ if (rangeType != TAIL) {
+ try {
+ smap.subMap(makeKey(rangeBegin), makeKey(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ smap.headMap(makeKey(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ checkDupsSize(0, smap.duplicates(makeKey(rangeEnd + 2)));
+ }
+ if (rangeType != HEAD) {
+ try {
+ smap.subMap(makeKey(rangeBegin - 1), makeKey(rangeEnd + 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ smap.tailMap(makeKey(rangeBegin - 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ checkDupsSize(0, smap.duplicates(makeKey(rangeBegin - 1)));
+ }
+
+ // keySet
+
+ if (rangeType != TAIL) {
+ SortedSet sset = (SortedSet) map.keySet();
+ try {
+ sset.subSet(makeKey(rangeBegin), makeKey(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.headSet(makeKey(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ iterator(sset.subSet(makeKey(rangeEnd + 1),
+ makeKey(rangeEnd + 2)));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+ if (rangeType != HEAD) {
+ SortedSet sset = (SortedSet) map.keySet();
+ try {
+ sset.subSet(makeKey(rangeBegin - 1), makeKey(rangeEnd + 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.tailSet(makeKey(rangeBegin - 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ iterator(sset.subSet(makeKey(rangeBegin - 1),
+ makeKey(rangeBegin)));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+
+ // entrySet
+
+ if (rangeType != TAIL) {
+ SortedSet sset = (SortedSet) map.entrySet();
+ try {
+ sset.subSet(mapEntry(rangeBegin), mapEntry(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.headSet(mapEntry(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ iterator(sset.subSet(mapEntry(rangeEnd + 1),
+ mapEntry(rangeEnd + 2)));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+ if (rangeType != HEAD) {
+ SortedSet sset = (SortedSet) map.entrySet();
+ try {
+ sset.subSet(mapEntry(rangeBegin - 1), mapEntry(rangeEnd + 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.tailSet(mapEntry(rangeBegin - 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ iterator(sset.subSet(mapEntry(rangeBegin - 1),
+ mapEntry(rangeBegin)));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+
+ // values
+
+ if (map.values() instanceof SortedSet) {
+ SortedSet sset = (SortedSet) map.values();
+ if (rangeType != TAIL) {
+ try {
+ sset.subSet(makeVal(rangeBegin),
+ makeVal(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.headSet(makeVal(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+ if (rangeType != HEAD) {
+ try {
+ sset.subSet(makeVal(rangeBegin - 1),
+ makeVal(rangeEnd + 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.tailSet(makeVal(rangeBegin - 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+ }
+
+ // list
+
+ if (list != null) {
+ int size = rangeEnd - rangeBegin + 1;
+ try {
+ list.subList(0, size + 1);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ try {
+ list.subList(-1, size);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ try {
+ list.subList(2, 1);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ try {
+ list.subList(size, size);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ }
+ }
+
+ void writeOutOfRange(Long badNewKey) {
+ try {
+ map.put(badNewKey, makeVal(badNewKey));
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.toString(), index == null);
+ } catch (UnsupportedOperationException e) {
+ assertTrue(index != null);
+ }
+ try {
+ map.keySet().add(badNewKey);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(index == null);
+ } catch (UnsupportedOperationException e) {
+ assertTrue(index != null);
+ }
+ try {
+ map.values().add(makeEntity(badNewKey));
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(isEntityBinding && index == null);
+ } catch (UnsupportedOperationException e) {
+ assertTrue(!(isEntityBinding && index == null));
+ }
+ if (list != null) {
+ int i = badNewKey.intValue() - beginKey;
+ try {
+ list.set(i, makeVal(i));
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ assertTrue(index == null);
+ } catch (UnsupportedOperationException e) {
+ assertTrue(index != null);
+ }
+ try {
+ list.add(i, makeVal(badNewKey));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+ }
+
+ void readWriteDuplicates()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ if (index == null) {
+ readWritePrimaryDuplicates(beginKey);
+ readWritePrimaryDuplicates(beginKey + 1);
+ readWritePrimaryDuplicates(endKey);
+ readWritePrimaryDuplicates(endKey - 1);
+ } else {
+ readWriteIndexedDuplicates(beginKey);
+ readWriteIndexedDuplicates(beginKey + 1);
+ readWriteIndexedDuplicates(endKey);
+ readWriteIndexedDuplicates(endKey - 1);
+ }
+ }
+ });
+ }
+
+ void readWritePrimaryDuplicates(int i)
+ throws Exception {
+
+ Collection dups;
+ // make duplicate values
+ final Long key = makeKey(i);
+ final Object[] values = new Object[5];
+ for (int j = 0; j < values.length; j += 1) {
+ values[j] = isEntityBinding
+ ? makeEntity(i, i + j)
+ : makeVal(i + j);
+ }
+ // add duplicates
+ outerLoop: for (int writeMode = 0;; writeMode += 1) {
+ //System.out.println("write mode " + writeMode);
+ switch (writeMode) {
+ case 0:
+ case 1: {
+ // write with Map.put()
+ for (int j = 1; j < values.length; j += 1) {
+ map.put(key, values[j]);
+ }
+ break;
+ }
+ case 2: {
+ // write with Map.duplicates().add()
+ dups = map.duplicates(key);
+ for (int j = 1; j < values.length; j += 1) {
+ dups.add(values[j]);
+ }
+ break;
+ }
+ case 3: {
+ // write with Map.duplicates().iterator().add()
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ Collection dups = map.duplicates(key);
+ Iterator iter = iterator(dups);
+ assertEquals(values[0], iter.next());
+ assertTrue(!iter.hasNext());
+ try {
+ for (int j = 1; j < values.length; j += 1) {
+ ((ListIterator) iter).add(values[j]);
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ break;
+ }
+ case 4: {
+ // write with Map.values().add()
+ if (!isEntityBinding) {
+ continue;
+ }
+ Collection set = map.values();
+ for (int j = 1; j < values.length; j += 1) {
+ set.add(values[j]);
+ }
+ break;
+ }
+ default: {
+ break outerLoop;
+ }
+ }
+ checkDupsSize(values.length, map.duplicates(key));
+ // read duplicates
+ readDuplicates(i, key, values);
+ // remove duplicates
+ switch (writeMode) {
+ case 0: {
+ // remove with Map.remove()
+ checkDupsSize(values.length, map.duplicates(key));
+ map.remove(key); // remove all values
+ checkDupsSize(0, map.duplicates(key));
+ map.put(key, values[0]); // put back original value
+ checkDupsSize(1, map.duplicates(key));
+ break;
+ }
+ case 1: {
+ // remove with Map.keySet().remove()
+ map.keySet().remove(key); // remove all values
+ map.put(key, values[0]); // put back original value
+ break;
+ }
+ case 2: {
+ // remove with Map.duplicates().clear()
+ dups = map.duplicates(key);
+ dups.clear(); // remove all values
+ dups.add(values[0]); // put back original value
+ break;
+ }
+ case 3: {
+ // remove with Map.duplicates().iterator().remove()
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ Collection dups = map.duplicates(key);
+ Iterator iter = iterator(dups);
+ try {
+ for (int j = 0; j < values.length; j += 1) {
+ assertEquals(values[j], iter.next());
+ if (j != 0) {
+ iter.remove();
+ }
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ break;
+ }
+ case 4: {
+ // remove with Map.values().remove()
+ if (!isEntityBinding) {
+ throw new IllegalStateException();
+ }
+ Collection set = map.values();
+ for (int j = 1; j < values.length; j += 1) {
+ set.remove(values[j]);
+ }
+ break;
+ }
+ default: throw new IllegalStateException();
+ }
+ // verify that only original value is present
+ dups = map.duplicates(key);
+ assertTrue(dups.contains(values[0]));
+ for (int j = 1; j < values.length; j += 1) {
+ assertTrue(!dups.contains(values[j]));
+ }
+ checkDupsSize(1, dups);
+ }
+ }
+
+ void readWriteIndexedDuplicates(int i) {
+ Object key = makeKey(i);
+ Object[] values = new Object[3];
+ values[0] = makeVal(i);
+ for (int j = 1; j < values.length; j += 1) {
+ values[j] = isEntityBinding
+ ? makeEntity(endKey + j, i)
+ : makeVal(i);
+ }
+ // add duplicates
+ for (int j = 1; j < values.length; j += 1) {
+ imap.put(makeKey(endKey + j), values[j]);
+ }
+ // read duplicates
+ readDuplicates(i, key, values);
+ // remove duplicates
+ for (int j = 1; j < values.length; j += 1) {
+ imap.remove(makeKey(endKey + j));
+ }
+ checkDupsSize(1, map.duplicates(key));
+ }
+
+ void readDuplicates(int i, Object key, Object[] values) {
+
+ boolean isOrdered = map.isOrdered();
+ Collection dups;
+ Iterator iter;
+ // read with Map.duplicates().iterator()
+ dups = map.duplicates(key);
+ checkDupsSize(values.length, dups);
+ iter = iterator(dups);
+ try {
+ for (int j = 0; j < values.length; j += 1) {
+ assertTrue(iter.hasNext());
+ Object val = iter.next();
+ assertEquals(values[j], val);
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ // read with Map.values().iterator()
+ Collection clone = ((StoredCollection) map.values()).toList();
+ iter = iterator(map.values());
+ try {
+ for (int j = beginKey; j < i; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(makeVal(j)));
+ if (isOrdered) {
+ assertEquals(makeVal(j), val);
+ }
+ }
+ for (int j = 0; j < values.length; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(values[j]));
+ if (isOrdered) {
+ assertEquals(values[j], val);
+ }
+ }
+ for (int j = i + 1; j <= endKey; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(makeVal(j)));
+ if (isOrdered) {
+ assertEquals(makeVal(j), val);
+ }
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(clone.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ // read with Map.entrySet().iterator()
+ clone = ((StoredCollection) map.entrySet()).toList();
+ iter = iterator(map.entrySet());
+ try {
+ for (int j = beginKey; j < i; j += 1) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ assertTrue(clone.remove(mapEntry(j)));
+ if (isOrdered) {
+ assertEquals(makeVal(j), entry.getValue());
+ assertEquals(makeKey(j), entry.getKey());
+ }
+ }
+ for (int j = 0; j < values.length; j += 1) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ assertTrue(clone.remove(mapEntry(makeKey(i), values[j])));
+ if (isOrdered) {
+ assertEquals(values[j], entry.getValue());
+ assertEquals(makeKey(i), entry.getKey());
+ }
+ }
+ for (int j = i + 1; j <= endKey; j += 1) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ assertTrue(clone.remove(mapEntry(j)));
+ if (isOrdered) {
+ assertEquals(makeVal(j), entry.getValue());
+ assertEquals(makeKey(j), entry.getKey());
+ }
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(clone.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ // read with Map.keySet().iterator()
+ clone = ((StoredCollection) map.keySet()).toList();
+ iter = iterator(map.keySet());
+ try {
+ for (int j = beginKey; j < i; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(makeKey(j)));
+ if (isOrdered) {
+ assertEquals(makeKey(j), val);
+ }
+ }
+ if (true) {
+ // only one key is iterated for all duplicates
+ Object val = iter.next();
+ assertTrue(clone.remove(makeKey(i)));
+ if (isOrdered) {
+ assertEquals(makeKey(i), val);
+ }
+ }
+ for (int j = i + 1; j <= endKey; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(makeKey(j)));
+ if (isOrdered) {
+ assertEquals(makeKey(j), val);
+ }
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(clone.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+
+ void duplicatesNotAllowed() {
+
+ Collection dups = map.duplicates(makeKey(beginKey));
+ try {
+ dups.add(makeVal(beginKey));
+ fail();
+ } catch (UnsupportedOperationException expected) { }
+ ListIterator iter = (ListIterator) iterator(dups);
+ try {
+ iter.add(makeVal(beginKey));
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+
+ void listOperationsNotAllowed() {
+
+ ListIterator iter = (ListIterator) iterator(map.values());
+ try {
+ try {
+ iter.nextIndex();
+ fail();
+ } catch (UnsupportedOperationException expected) { }
+ try {
+ iter.previousIndex();
+ fail();
+ } catch (UnsupportedOperationException expected) { }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+
+ void testCdbLocking() {
+
+ Iterator readIterator;
+ Iterator writeIterator;
+ StoredKeySet set = (StoredKeySet) map.keySet();
+
+ // can open two CDB read cursors
+ readIterator = set.storedIterator(false);
+ try {
+ Iterator readIterator2 = set.storedIterator(false);
+ StoredIterator.close(readIterator2);
+ } finally {
+ StoredIterator.close(readIterator);
+ }
+
+ // can open two CDB write cursors
+ writeIterator = set.storedIterator(true);
+ try {
+ Iterator writeIterator2 = set.storedIterator(true);
+ StoredIterator.close(writeIterator2);
+ } finally {
+ StoredIterator.close(writeIterator);
+ }
+
+ // cannot open CDB write cursor when read cursor is open,
+ readIterator = set.storedIterator(false);
+ try {
+ writeIterator = set.storedIterator(true);
+ fail();
+ StoredIterator.close(writeIterator);
+ } catch (IllegalStateException e) {
+ } finally {
+ StoredIterator.close(readIterator);
+ }
+
+ if (index == null) {
+ // cannot put() with read cursor open
+ readIterator = set.storedIterator(false);
+ try {
+ map.put(makeKey(1), makeVal(1));
+ fail();
+ } catch (IllegalStateException e) {
+ } finally {
+ StoredIterator.close(readIterator);
+ }
+
+ // cannot append() with write cursor open with RECNO/QUEUE only
+ writeIterator = set.storedIterator(true);
+ try {
+ if (testStore.isQueueOrRecno()) {
+ try {
+ map.append(makeVal(1));
+ fail();
+ } catch (IllegalStateException e) {}
+ } else {
+ map.append(makeVal(1));
+ }
+ } finally {
+ StoredIterator.close(writeIterator);
+ }
+ }
+ }
+
+ Object makeVal(int key) {
+
+ if (isEntityBinding) {
+ return makeEntity(key);
+ } else {
+ return new Long(key + 100);
+ }
+ }
+
+ Object makeVal(int key, int val) {
+
+ if (isEntityBinding) {
+ return makeEntity(key, val);
+ } else {
+ return makeVal(val);
+ }
+ }
+
+ Object makeEntity(int key, int val) {
+
+ return new TestEntity(key, val + 100);
+ }
+
+ int intVal(Object val) {
+
+ if (isEntityBinding) {
+ return ((TestEntity) val).value - 100;
+ } else {
+ return ((Long) val).intValue() - 100;
+ }
+ }
+
+ int intKey(Object key) {
+
+ return ((Long) key).intValue();
+ }
+
+ Object makeVal(Long key) {
+
+ return makeVal(key.intValue());
+ }
+
+ Object makeEntity(int key) {
+
+ return makeEntity(key, key);
+ }
+
+ Object makeEntity(Long key) {
+
+ return makeEntity(key.intValue());
+ }
+
+ int intIter(Collection coll, Object value) {
+
+ if (coll instanceof StoredKeySet) {
+ return intKey(value);
+ } else {
+ if (coll instanceof StoredEntrySet) {
+ value = ((Map.Entry) value).getValue();
+ }
+ return intVal(value);
+ }
+ }
+
+ Map.Entry mapEntry(Object key, Object val) {
+
+ return new MapEntryParameter(key, val);
+ }
+
+ Map.Entry mapEntry(int key) {
+
+ return new MapEntryParameter(makeKey(key), makeVal(key));
+ }
+
+ Long makeKey(int key) {
+
+ return new Long(key);
+ }
+
+ boolean isSubMap() {
+
+ return rangeType != NONE;
+ }
+
+ void checkDupsSize(int expected, Collection coll) {
+
+ assertEquals(expected, coll.size());
+ if (coll instanceof StoredCollection) {
+ StoredIterator i = ((StoredCollection) coll).storedIterator(false);
+ try {
+ int actual = 0;
+ if (i.hasNext()) {
+ i.next();
+ actual = i.count();
+ }
+ assertEquals(expected, actual);
+ } finally {
+ StoredIterator.close(i);
+ }
+ }
+ }
+
+ private boolean isListAddAllowed() {
+
+ return list != null && testStore.isQueueOrRecno() &&
+ list.areKeysRenumbered();
+ }
+
+ private int countElements(Collection coll) {
+
+ int count = 0;
+ Iterator iter = iterator(coll);
+ try {
+ while (iter.hasNext()) {
+ iter.next();
+ count += 1;
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ return count;
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/DbTestUtil.java b/test/scr024/src/com/sleepycat/collections/test/DbTestUtil.java
new file mode 100644
index 00000000..584982cc
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/DbTestUtil.java
@@ -0,0 +1,129 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.db.DatabaseConfig;
+
+/**
+ * @author Mark Hayes
+ */
+public class DbTestUtil {
+
+ public static final DatabaseConfig DBCONFIG_CREATE = new DatabaseConfig();
+ static {
+ DBCONFIG_CREATE.setAllowCreate(true);
+ }
+
+ private static final File TEST_DIR;
+ static {
+ String dir = System.getProperty("testdestdir");
+ if (dir == null || dir.length() == 0) {
+ dir = ".";
+ }
+ TEST_DIR = new File(dir, "tmp");
+ }
+
+ public static void printTestName(String name) {
+ // don't want verbose printing for now
+ // System.out.println(name);
+ }
+
+ public static File getExistingDir(String name)
+ throws IOException {
+
+ File dir = new File(TEST_DIR, name);
+ if (!dir.exists() || !dir.isDirectory()) {
+ throw new IllegalStateException(
+ "Not an existing directory: " + dir);
+ }
+ return dir;
+ }
+
+ public static File getNewDir()
+ throws IOException {
+
+ return getNewDir("test-dir");
+ }
+
+ public static File getNewDir(String name)
+ throws IOException {
+
+ File dir = new File(TEST_DIR, name);
+ if (dir.isDirectory()) {
+ String[] files = dir.list();
+ if (files != null) {
+ for (int i = 0; i < files.length; i += 1) {
+ new File(dir, files[i]).delete();
+ }
+ }
+ } else {
+ dir.delete();
+ dir.mkdirs();
+ }
+ return dir;
+ }
+
+ public static File getNewFile()
+ throws IOException {
+
+ return getNewFile("test-file");
+ }
+
+ public static File getNewFile(String name)
+ throws IOException {
+
+ return getNewFile(TEST_DIR, name);
+ }
+
+ public static File getNewFile(File dir, String name)
+ throws IOException {
+
+ File file = new File(dir, name);
+ file.delete();
+ return file;
+ }
+
+ public static boolean copyResource(Class cls, String fileName, File toDir)
+ throws IOException {
+
+ InputStream in = cls.getResourceAsStream("testdata/" + fileName);
+ if (in == null) {
+ return false;
+ }
+ in = new BufferedInputStream(in);
+ File file = new File(toDir, fileName);
+ OutputStream out = new FileOutputStream(file);
+ out = new BufferedOutputStream(out);
+ int c;
+ while ((c = in.read()) >= 0) out.write(c);
+ in.close();
+ out.close();
+ return true;
+ }
+
+ public static String qualifiedTestName(TestCase test) {
+
+ String s = test.getClass().getName();
+ int i = s.lastIndexOf('.');
+ if (i >= 0) {
+ s = s.substring(i + 1);
+ }
+ return s + '.' + test.getName();
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/ForeignKeyTest.java b/test/scr024/src/com/sleepycat/collections/test/ForeignKeyTest.java
new file mode 100644
index 00000000..8dc2759d
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/ForeignKeyTest.java
@@ -0,0 +1,342 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialMarshalledKeyCreator;
+import com.sleepycat.bind.serial.test.MarshalledObject;
+import com.sleepycat.collections.CurrentTransaction;
+import com.sleepycat.collections.TupleSerialFactory;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class ForeignKeyTest extends TestCase {
+
+ private static final ForeignKeyDeleteAction[] ACTIONS = {
+ ForeignKeyDeleteAction.ABORT,
+ ForeignKeyDeleteAction.NULLIFY,
+ ForeignKeyDeleteAction.CASCADE,
+ };
+ private static final String[] ACTION_LABELS = {
+ "ABORT",
+ "NULLIFY",
+ "CASCADE",
+ };
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ for (int j = 0; j < ACTIONS.length; j += 1) {
+ suite.addTest(new ForeignKeyTest(TestEnv.ALL[i],
+ ACTIONS[j],
+ ACTION_LABELS[j]));
+ }
+ }
+ return suite;
+ }
+
+ private TestEnv testEnv;
+ private Environment env;
+ private StoredClassCatalog catalog;
+ private TupleSerialFactory factory;
+ private Database store1;
+ private Database store2;
+ private SecondaryDatabase index1;
+ private SecondaryDatabase index2;
+ private Map storeMap1;
+ private Map storeMap2;
+ private Map indexMap1;
+ private Map indexMap2;
+ private final ForeignKeyDeleteAction onDelete;
+
+ public ForeignKeyTest(TestEnv testEnv, ForeignKeyDeleteAction onDelete,
+ String onDeleteLabel) {
+
+ super("ForeignKeyTest-" + testEnv.getName() + '-' + onDeleteLabel);
+
+ this.testEnv = testEnv;
+ this.onDelete = onDelete;
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = testEnv.open(getName());
+
+ createDatabase();
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (index1 != null) {
+ index1.close();
+ }
+ if (index2 != null) {
+ index2.close();
+ }
+ if (store1 != null) {
+ store1.close();
+ }
+ if (store2 != null) {
+ store2.close();
+ }
+ if (catalog != null) {
+ catalog.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ env = null;
+ testEnv = null;
+ catalog = null;
+ store1 = null;
+ store2 = null;
+ index1 = null;
+ index2 = null;
+ factory = null;
+ storeMap1 = null;
+ storeMap2 = null;
+ indexMap1 = null;
+ indexMap2 = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ try {
+ createViews();
+ writeAndRead();
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ }
+ }
+
+ private void createDatabase()
+ throws Exception {
+
+ catalog = new StoredClassCatalog(openDb("catalog.db"));
+ factory = new TupleSerialFactory(catalog);
+ assertSame(catalog, factory.getCatalog());
+
+ store1 = openDb("store1.db");
+ store2 = openDb("store2.db");
+ index1 = openSecondaryDb(factory, "1", store1, "index1.db", null);
+ index2 = openSecondaryDb(factory, "2", store2, "index2.db", store1);
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(testEnv.isTxnMode());
+ config.setAllowCreate(true);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ private SecondaryDatabase openSecondaryDb(TupleSerialFactory factory,
+ String keyName,
+ Database primary,
+ String file,
+ Database foreignStore)
+ throws Exception {
+
+ TupleSerialMarshalledKeyCreator keyCreator =
+ factory.getKeyCreator(MarshalledObject.class, keyName);
+
+ SecondaryConfig secConfig = new SecondaryConfig();
+ DbCompat.setTypeBtree(secConfig);
+ secConfig.setTransactional(testEnv.isTxnMode());
+ secConfig.setAllowCreate(true);
+ secConfig.setKeyCreator(keyCreator);
+ if (foreignStore != null) {
+ secConfig.setForeignKeyDatabase(foreignStore);
+ secConfig.setForeignKeyDeleteAction(onDelete);
+ if (onDelete == ForeignKeyDeleteAction.NULLIFY) {
+ secConfig.setForeignKeyNullifier(keyCreator);
+ }
+ }
+
+ return DbCompat.testOpenSecondaryDatabase
+ (env, null, file, null, primary, secConfig);
+ }
+
+ private void createViews() {
+ storeMap1 = factory.newMap(store1, String.class,
+ MarshalledObject.class, true);
+ storeMap2 = factory.newMap(store2, String.class,
+ MarshalledObject.class, true);
+ indexMap1 = factory.newMap(index1, String.class,
+ MarshalledObject.class, true);
+ indexMap2 = factory.newMap(index2, String.class,
+ MarshalledObject.class, true);
+ }
+
+ private void writeAndRead()
+ throws Exception {
+
+ CurrentTransaction txn = CurrentTransaction.getInstance(env);
+ if (txn != null) {
+ txn.beginTransaction(null);
+ }
+
+ MarshalledObject o1 = new MarshalledObject("data1", "pk1", "ik1", "");
+ assertNull(storeMap1.put(null, o1));
+
+ assertEquals(o1, storeMap1.get("pk1"));
+ assertEquals(o1, indexMap1.get("ik1"));
+
+ MarshalledObject o2 = new MarshalledObject("data2", "pk2", "", "pk1");
+ assertNull(storeMap2.put(null, o2));
+
+ assertEquals(o2, storeMap2.get("pk2"));
+ assertEquals(o2, indexMap2.get("pk1"));
+
+ if (txn != null) {
+ txn.commitTransaction();
+ txn.beginTransaction(null);
+ }
+
+ /*
+ * store1 contains o1 with primary key "pk1" and index key "ik1".
+ *
+ * store2 contains o2 with primary key "pk2" and foreign key "pk1",
+ * which is the primary key of store1.
+ */
+
+ if (onDelete == ForeignKeyDeleteAction.ABORT) {
+
+ /* Test that we abort trying to delete a referenced key. */
+
+ try {
+ storeMap1.remove("pk1");
+ fail();
+ } catch (RuntimeExceptionWrapper expected) {
+ assertTrue(expected.getCause() instanceof DatabaseException);
+ assertTrue(!DbCompat.NEW_JE_EXCEPTIONS);
+ }
+ if (txn != null) {
+ txn.abortTransaction();
+ txn.beginTransaction(null);
+ }
+
+ /* Test that we can put a record into store2 with a null foreign
+ * key value. */
+
+ o2 = new MarshalledObject("data2", "pk2", "", "");
+ assertNotNull(storeMap2.put(null, o2));
+ assertEquals(o2, storeMap2.get("pk2"));
+
+ /* The index2 record should have been deleted since the key was set
+ * to null above. */
+
+ assertNull(indexMap2.get("pk1"));
+
+ /* Test that now we can delete the record in store1, since it is no
+ * longer referenced. */
+
+ assertNotNull(storeMap1.remove("pk1"));
+ assertNull(storeMap1.get("pk1"));
+ assertNull(indexMap1.get("ik1"));
+
+ } else if (onDelete == ForeignKeyDeleteAction.NULLIFY) {
+
+ /* Delete the referenced key. */
+
+ assertNotNull(storeMap1.remove("pk1"));
+ assertNull(storeMap1.get("pk1"));
+ assertNull(indexMap1.get("ik1"));
+
+ /* The store2 record should still exist, but should have an empty
+ * secondary key since it was nullified. */
+
+ o2 = (MarshalledObject) storeMap2.get("pk2");
+ assertNotNull(o2);
+ assertEquals("data2", o2.getData());
+ assertEquals("pk2", o2.getPrimaryKey());
+ assertEquals("", o2.getIndexKey1());
+ assertEquals("", o2.getIndexKey2());
+
+ } else if (onDelete == ForeignKeyDeleteAction.CASCADE) {
+
+ /* Delete the referenced key. */
+
+ assertNotNull(storeMap1.remove("pk1"));
+ assertNull(storeMap1.get("pk1"));
+ assertNull(indexMap1.get("ik1"));
+
+ /* The store2 record should have deleted also. */
+
+ assertNull(storeMap2.get("pk2"));
+ assertNull(indexMap2.get("pk1"));
+
+ } else {
+ throw new IllegalStateException();
+ }
+
+ /*
+ * Test that a foreign key value may not be used that is not present
+ * in the foreign store. "pk2" is not in store1 in this case.
+ */
+ assertNull(storeMap1.get("pk2"));
+ MarshalledObject o3 = new MarshalledObject("data3", "pk3", "", "pk2");
+ try {
+ storeMap2.put(null, o3);
+ fail();
+ } catch (RuntimeExceptionWrapper expected) {
+ assertTrue(expected.getCause() instanceof DatabaseException);
+ assertTrue(!DbCompat.NEW_JE_EXCEPTIONS);
+ }
+
+ if (txn != null) {
+ txn.abortTransaction();
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/IterDeadlockTest.java b/test/scr024/src/com/sleepycat/collections/test/IterDeadlockTest.java
new file mode 100644
index 00000000..59509e9c
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/IterDeadlockTest.java
@@ -0,0 +1,228 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.ByteArrayBinding;
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Tests the fix for [#10516], where the StoredIterator constructor was not
+ * closing the cursor when an exception occurred. For example, a deadlock
+ * exception might occur if the constructor was unable to move the cursor to
+ * the first element.
+ * @author Mark Hayes
+ */
+public class IterDeadlockTest extends TestCase {
+
+ private static final byte[] ONE = { 1 };
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(IterDeadlockTest.class);
+ return suite;
+ }
+
+ private Environment env;
+ private Database store1;
+ private Database store2;
+ private StoredSortedMap map1;
+ private StoredSortedMap map2;
+ private final ByteArrayBinding binding = new ByteArrayBinding();
+
+ public IterDeadlockTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ env = TestEnv.TXN.open("IterDeadlockTest");
+ store1 = openDb("store1.db");
+ store2 = openDb("store2.db");
+ map1 = new StoredSortedMap(store1, binding, binding, true);
+ map2 = new StoredSortedMap(store2, binding, binding, true);
+ }
+
+ @Override
+ public void tearDown() {
+
+ if (store1 != null) {
+ try {
+ store1.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ if (store2 != null) {
+ try {
+ store2.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ if (env != null) {
+ try {
+ env.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ /* Allow GC of DB objects in the test case. */
+ env = null;
+ store1 = null;
+ store2 = null;
+ map1 = null;
+ map2 = null;
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(true);
+ config.setAllowCreate(true);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ public void testIterDeadlock()
+ throws Exception {
+
+ final Object parent = new Object();
+ final Object child1 = new Object();
+ final Object child2 = new Object();
+ final TransactionRunner runner = new TransactionRunner(env);
+ runner.setMaxRetries(0);
+
+ /* Write a record in each db. */
+ runner.run(new TransactionWorker() {
+ public void doWork() {
+ assertNull(map1.put(ONE, ONE));
+ assertNull(map2.put(ONE, ONE));
+ }
+ });
+
+ /*
+ * A thread to open iterator 1, then wait to be notified, then open
+ * iterator 2.
+ */
+ final Thread thread1 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ synchronized (child1) {
+ ListIterator i1 =
+ (ListIterator) map1.values().iterator();
+ i1.next();
+ i1.set(ONE); /* Write lock. */
+ StoredIterator.close(i1);
+ synchronized (parent) { parent.notify(); }
+ child1.wait();
+ Iterator i2 = map2.values().iterator();
+ assertTrue(i2.hasNext());
+ StoredIterator.close(i2);
+ }
+ }
+ });
+ } catch (DeadlockException expected) {
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.toString());
+ }
+ }
+ });
+
+ /*
+ * A thread to open iterator 2, then wait to be notified, then open
+ * iterator 1.
+ */
+ final Thread thread2 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ synchronized (child2) {
+ ListIterator i2 =
+ (ListIterator) map2.values().iterator();
+ i2.next();
+ i2.set(ONE); /* Write lock. */
+ StoredIterator.close(i2);
+ synchronized (parent) { parent.notify(); }
+ child2.wait();
+ Iterator i1 = map1.values().iterator();
+ assertTrue(i1.hasNext());
+ StoredIterator.close(i1);
+ }
+ }
+ });
+ } catch (DeadlockException expected) {
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.toString());
+ }
+ }
+ });
+
+ /*
+ * Open iterator 1 in thread 1, then iterator 2 in thread 2, then let
+ * the threads run to open the other iterators and cause a deadlock.
+ */
+ synchronized (parent) {
+ thread1.start();
+ parent.wait();
+ thread2.start();
+ parent.wait();
+ synchronized (child1) { child1.notify(); }
+ synchronized (child2) { child2.notify(); }
+ thread1.join();
+ thread2.join();
+ }
+
+ /*
+ * Before the fix for [#10516] we would get an exception indicating
+ * that cursors were not closed, when closing the stores below.
+ */
+ store1.close();
+ store1 = null;
+ store2.close();
+ store2 = null;
+ env.close();
+ env = null;
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/JoinTest.java b/test/scr024/src/com/sleepycat/collections/test/JoinTest.java
new file mode 100644
index 00000000..495f7854
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/JoinTest.java
@@ -0,0 +1,225 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.test.MarshalledObject;
+import com.sleepycat.collections.StoredCollection;
+import com.sleepycat.collections.StoredContainer;
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.collections.TupleSerialFactory;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class JoinTest extends TestCase
+ implements TransactionWorker {
+
+ private static final String MATCH_DATA = "d4"; // matches both keys = "yes"
+ private static final String MATCH_KEY = "k4"; // matches both keys = "yes"
+ private static final String[] VALUES = {"yes", "yes"};
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ return new JoinTest();
+ }
+
+ private Environment env;
+ private TransactionRunner runner;
+ private StoredClassCatalog catalog;
+ private TupleSerialFactory factory;
+ private Database store;
+ private SecondaryDatabase index1;
+ private SecondaryDatabase index2;
+ private StoredMap storeMap;
+ private StoredMap indexMap1;
+ private StoredMap indexMap2;
+
+ public JoinTest() {
+
+ super("JoinTest");
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = TestEnv.TXN.open(getName());
+ runner = new TransactionRunner(env);
+ createDatabase();
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (index1 != null) {
+ index1.close();
+ }
+ if (index2 != null) {
+ index2.close();
+ }
+ if (store != null) {
+ store.close();
+ }
+ if (catalog != null) {
+ catalog.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ index1 = null;
+ index2 = null;
+ store = null;
+ catalog = null;
+ env = null;
+ runner = null;
+ factory = null;
+ storeMap = null;
+ indexMap1 = null;
+ indexMap2 = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ runner.run(this);
+ }
+
+ public void doWork() {
+ createViews();
+ writeAndRead();
+ }
+
+ private void createDatabase()
+ throws Exception {
+
+ catalog = new StoredClassCatalog(openDb("catalog.db"));
+ factory = new TupleSerialFactory(catalog);
+ assertSame(catalog, factory.getCatalog());
+
+ store = openDb("store.db");
+ index1 = openSecondaryDb(store, "index1.db", "1");
+ index2 = openSecondaryDb(store, "index2.db", "2");
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(true);
+ config.setAllowCreate(true);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ private SecondaryDatabase openSecondaryDb(Database primary,
+ String file,
+ String keyName)
+ throws Exception {
+
+ SecondaryConfig secConfig = new SecondaryConfig();
+ DbCompat.setTypeBtree(secConfig);
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ DbCompat.setSortedDuplicates(secConfig, true);
+ secConfig.setKeyCreator(factory.getKeyCreator(MarshalledObject.class,
+ keyName));
+
+ return DbCompat.testOpenSecondaryDatabase
+ (env, null, file, null, primary, secConfig);
+ }
+
+ private void createViews() {
+ storeMap = factory.newMap(store, String.class,
+ MarshalledObject.class, true);
+ indexMap1 = factory.newMap(index1, String.class,
+ MarshalledObject.class, true);
+ indexMap2 = factory.newMap(index2, String.class,
+ MarshalledObject.class, true);
+ }
+
+ private void writeAndRead() {
+ // write records: Data, PrimaryKey, IndexKey1, IndexKey2
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d1", "k1", "no", "yes")));
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d2", "k2", "no", "no")));
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d3", "k3", "no", "yes")));
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d4", "k4", "yes", "yes")));
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d5", "k5", "yes", "no")));
+
+ Object o;
+ Map.Entry e;
+
+ // join values with index maps
+ o = doJoin((StoredCollection) storeMap.values());
+ assertEquals(MATCH_DATA, ((MarshalledObject) o).getData());
+
+ // join keySet with index maps
+ o = doJoin((StoredCollection) storeMap.keySet());
+ assertEquals(MATCH_KEY, o);
+
+ // join entrySet with index maps
+ o = doJoin((StoredCollection) storeMap.entrySet());
+ e = (Map.Entry) o;
+ assertEquals(MATCH_KEY, e.getKey());
+ assertEquals(MATCH_DATA, ((MarshalledObject) e.getValue()).getData());
+ }
+
+ private Object doJoin(StoredCollection coll) {
+
+ StoredContainer[] indices = { indexMap1, indexMap2 };
+ StoredIterator i = coll.join(indices, VALUES, null);
+ try {
+ assertTrue(i.hasNext());
+ Object result = i.next();
+ assertNotNull(result);
+ assertFalse(i.hasNext());
+ return result;
+ } finally { i.close(); }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/NullTransactionRunner.java b/test/scr024/src/com/sleepycat/collections/test/NullTransactionRunner.java
new file mode 100644
index 00000000..2ccfd668
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/NullTransactionRunner.java
@@ -0,0 +1,32 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.ExceptionUnwrapper;
+
+class NullTransactionRunner extends TransactionRunner {
+
+ NullTransactionRunner(Environment env) {
+
+ super(env);
+ }
+
+ public void run(TransactionWorker worker)
+ throws Exception {
+
+ try {
+ worker.doWork();
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/SecondaryDeadlockTest.java b/test/scr024/src/com/sleepycat/collections/test/SecondaryDeadlockTest.java
new file mode 100644
index 00000000..b52e2cb2
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/SecondaryDeadlockTest.java
@@ -0,0 +1,206 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.db.TransactionConfig;
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Tests whether secondary access can cause a self-deadlock when reading via a
+ * secondary because the collections API secondary implementation in DB 4.2
+ * opens two cursors. Part of the problem in [#10516] was because the
+ * secondary get() was not done in a txn. This problem should not occur in DB
+ * 4.3 and JE -- an ordinary deadlock occurs instead and is detected.
+ *
+ * @author Mark Hayes
+ */
+public class SecondaryDeadlockTest extends TestCase {
+
+ private static final Long N_ONE = new Long(1);
+ private static final Long N_101 = new Long(101);
+ private static final int N_ITERS = 20;
+ private static final int MAX_RETRIES = 1000;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(SecondaryDeadlockTest.class);
+ return suite;
+ }
+
+ private Environment env;
+ private Database store;
+ private Database index;
+ private StoredSortedMap storeMap;
+ private StoredSortedMap indexMap;
+ private Exception exception;
+
+ public SecondaryDeadlockTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ env = TestEnv.TXN.open("SecondaryDeadlockTest");
+ store = TestStore.BTREE_UNIQ.open(env, "store.db");
+ index = TestStore.BTREE_UNIQ.openIndex(store, "index.db");
+ storeMap = new StoredSortedMap(store,
+ TestStore.BTREE_UNIQ.getKeyBinding(),
+ TestStore.BTREE_UNIQ.getValueBinding(),
+ true);
+ indexMap = new StoredSortedMap(index,
+ TestStore.BTREE_UNIQ.getKeyBinding(),
+ TestStore.BTREE_UNIQ.getValueBinding(),
+ true);
+ }
+
+ @Override
+ public void tearDown() {
+
+ if (index != null) {
+ try {
+ index.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ if (store != null) {
+ try {
+ store.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ if (env != null) {
+ try {
+ env.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ /* Allow GC of DB objects in the test case. */
+ env = null;
+ store = null;
+ index = null;
+ storeMap = null;
+ indexMap = null;
+ }
+
+ public void testSecondaryDeadlock()
+ throws Exception {
+
+ final TransactionRunner runner = new TransactionRunner(env);
+ runner.setMaxRetries(MAX_RETRIES);
+
+ /*
+ * This test deadlocks a lot at degree 3 serialization. In debugging
+ * this I discovered it was not due to phantom prevention per se but
+ * just to a change in timing.
+ */
+ TransactionConfig txnConfig = new TransactionConfig();
+ runner.setTransactionConfig(txnConfig);
+
+ /*
+ * A thread to do put() and delete() via the primary, which will lock
+ * the primary first then the secondary. Uses transactions.
+ */
+ final Thread thread1 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ /* The TransactionRunner performs retries. */
+ for (int i = 0; i < N_ITERS; i +=1 ) {
+ runner.run(new TransactionWorker() {
+ public void doWork() {
+ assertEquals(null, storeMap.put(N_ONE, N_101));
+ }
+ });
+ runner.run(new TransactionWorker() {
+ public void doWork() {
+ assertEquals(N_101, storeMap.remove(N_ONE));
+ }
+ });
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ exception = e;
+ }
+ }
+ }, "ThreadOne");
+
+ /*
+ * A thread to get() via the secondary, which will lock the secondary
+ * first then the primary. Does not use a transaction.
+ */
+ final Thread thread2 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ for (int i = 0; i < N_ITERS; i +=1 ) {
+ for (int j = 0; j < MAX_RETRIES; j += 1) {
+ try {
+ Object value = indexMap.get(N_ONE);
+ assertTrue(value == null ||
+ N_101.equals(value));
+ break;
+ } catch (Exception e) {
+ e = ExceptionUnwrapper.unwrap(e);
+ if (e instanceof DeadlockException) {
+ continue; /* Retry on deadlock. */
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ exception = e;
+ }
+ }
+ }, "ThreadTwo");
+
+ thread1.start();
+ thread2.start();
+ thread1.join();
+ thread2.join();
+
+ index.close();
+ index = null;
+ store.close();
+ store = null;
+ env.close();
+ env = null;
+
+ if (exception != null) {
+ fail(exception.toString());
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TestDataBinding.java b/test/scr024/src/com/sleepycat/collections/test/TestDataBinding.java
new file mode 100644
index 00000000..96cd41d4
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TestDataBinding.java
@@ -0,0 +1,33 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * @author Mark Hayes
+ */
+class TestDataBinding implements EntryBinding {
+
+ public Object entryToObject(DatabaseEntry data) {
+
+ if (data.getSize() != 1) {
+ throw new IllegalStateException("size=" + data.getSize());
+ }
+ byte val = data.getData()[data.getOffset()];
+ return new Long(val);
+ }
+
+ public void objectToEntry(Object object, DatabaseEntry data) {
+
+ byte val = ((Number) object).byteValue();
+ data.setData(new byte[] { val }, 0, 1);
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TestEntity.java b/test/scr024/src/com/sleepycat/collections/test/TestEntity.java
new file mode 100644
index 00000000..3c895d7b
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TestEntity.java
@@ -0,0 +1,44 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+/**
+ * @author Mark Hayes
+ */
+class TestEntity {
+
+ int key;
+ int value;
+
+ TestEntity(int key, int value) {
+
+ this.key = key;
+ this.value = value;
+ }
+
+ public boolean equals(Object o) {
+
+ try {
+ TestEntity e = (TestEntity) o;
+ return e.key == key && e.value == value;
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+
+ return key;
+ }
+
+ public String toString() {
+
+ return "[key " + key + " value " + value + ']';
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TestEntityBinding.java b/test/scr024/src/com/sleepycat/collections/test/TestEntityBinding.java
new file mode 100644
index 00000000..a6d76324
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TestEntityBinding.java
@@ -0,0 +1,63 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * @author Mark Hayes
+ */
+class TestEntityBinding implements EntityBinding {
+
+ private boolean isRecNum;
+
+ TestEntityBinding(boolean isRecNum) {
+
+ this.isRecNum = isRecNum;
+ }
+
+ public Object entryToObject(DatabaseEntry key, DatabaseEntry value) {
+
+ byte keyByte;
+ if (isRecNum) {
+ if (key.getSize() != 4) {
+ throw new IllegalStateException();
+ }
+ keyByte = (byte) RecordNumberBinding.entryToRecordNumber(key);
+ } else {
+ if (key.getSize() != 1) {
+ throw new IllegalStateException();
+ }
+ keyByte = key.getData()[key.getOffset()];
+ }
+ if (value.getSize() != 1) {
+ throw new IllegalStateException();
+ }
+ byte valByte = value.getData()[value.getOffset()];
+ return new TestEntity(keyByte, valByte);
+ }
+
+ public void objectToKey(Object object, DatabaseEntry key) {
+
+ byte val = (byte) ((TestEntity) object).key;
+ if (isRecNum) {
+ RecordNumberBinding.recordNumberToEntry(val, key);
+ } else {
+ key.setData(new byte[] { val }, 0, 1);
+ }
+ }
+
+ public void objectToData(Object object, DatabaseEntry value) {
+
+ byte val = (byte) ((TestEntity) object).value;
+ value.setData(new byte[] { val }, 0, 1);
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TestEnv.java b/test/scr024/src/com/sleepycat/collections/test/TestEnv.java
new file mode 100644
index 00000000..aed6b5ae
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TestEnv.java
@@ -0,0 +1,130 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class TestEnv {
+
+ public static final TestEnv BDB;
+ public static final TestEnv CDB;
+ public static final TestEnv TXN;
+ static {
+ EnvironmentConfig config;
+
+ config = newEnvConfig();
+ BDB = new TestEnv("bdb", config);
+
+ if (DbCompat.CDB) {
+ config = newEnvConfig();
+ DbCompat.setInitializeCDB(config, true);
+ CDB = new TestEnv("cdb", config);
+ } else {
+ CDB = null;
+ }
+
+ config = newEnvConfig();
+ config.setTransactional(true);
+ DbCompat.setInitializeLocking(config, true);
+ TXN = new TestEnv("txn", config);
+ }
+
+ private static EnvironmentConfig newEnvConfig() {
+
+ EnvironmentConfig config = new EnvironmentConfig();
+ if (DbCompat.MEMORY_SUBSYSTEM) {
+ DbCompat.setInitializeCache(config, true);
+ }
+ return config;
+ }
+
+ public static final TestEnv[] ALL;
+ static {
+ if (DbCompat.CDB) {
+ ALL = new TestEnv[] { BDB, CDB, TXN };
+ } else {
+ ALL = new TestEnv[] { BDB, TXN };
+ }
+ }
+
+ private String name;
+ private EnvironmentConfig config;
+
+ TestEnv(String name, EnvironmentConfig config) {
+
+ this.name = name;
+ this.config = config;
+ }
+
+ public String getName() {
+
+ return name;
+ }
+
+ public boolean isTxnMode() {
+
+ return config.getTransactional();
+ }
+
+ public boolean isCdbMode() {
+
+ return DbCompat.getInitializeCDB(config);
+ }
+
+ public Environment open(String testName)
+ throws IOException, DatabaseException {
+
+ return open(testName, true);
+ }
+
+ public Environment open(String testName, boolean create)
+ throws IOException, DatabaseException {
+
+ config.setAllowCreate(create);
+ /* OLDEST deadlock detection on DB matches the use of timeouts on JE.*/
+ DbCompat.setLockDetectModeOldest(config);
+ File dir = getDirectory(testName, create);
+ return newEnvironment(dir, config);
+ }
+
+ /**
+ * Is overridden in XACollectionTest.
+ */
+ protected Environment newEnvironment(File dir, EnvironmentConfig config)
+ throws DatabaseException, IOException {
+
+ return new Environment(dir, config);
+ }
+
+ public File getDirectory(String testName)
+ throws IOException {
+
+ return getDirectory(testName, true);
+ }
+
+ public File getDirectory(String testName, boolean create)
+ throws IOException {
+
+ if (create) {
+ return SharedTestUtils.getNewDir("db-test/" + testName);
+ } else {
+ return SharedTestUtils.getExistingDir("db-test/" + testName);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TestKeyAssigner.java b/test/scr024/src/com/sleepycat/collections/test/TestKeyAssigner.java
new file mode 100644
index 00000000..0e5ea4f2
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TestKeyAssigner.java
@@ -0,0 +1,41 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.collections.PrimaryKeyAssigner;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * @author Mark Hayes
+ */
+class TestKeyAssigner implements PrimaryKeyAssigner {
+
+ private byte next = 1;
+ private final boolean isRecNum;
+
+ TestKeyAssigner(boolean isRecNum) {
+
+ this.isRecNum = isRecNum;
+ }
+
+ public void assignKey(DatabaseEntry keyData) {
+ if (isRecNum) {
+ RecordNumberBinding.recordNumberToEntry(next, keyData);
+ } else {
+ keyData.setData(new byte[] { next }, 0, 1);
+ }
+ next += 1;
+ }
+
+ void reset() {
+
+ next = 1;
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TestKeyCreator.java b/test/scr024/src/com/sleepycat/collections/test/TestKeyCreator.java
new file mode 100644
index 00000000..bcee3ef5
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TestKeyCreator.java
@@ -0,0 +1,56 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.db.SecondaryKeyCreator;
+
+/**
+ * Unused until secondaries are available.
+ * @author Mark Hayes
+ */
+class TestKeyCreator implements SecondaryKeyCreator {
+
+ private final boolean isRecNum;
+
+ TestKeyCreator(boolean isRecNum) {
+
+ this.isRecNum = isRecNum;
+ }
+
+ public boolean createSecondaryKey(SecondaryDatabase db,
+ DatabaseEntry primaryKeyData,
+ DatabaseEntry valueData,
+ DatabaseEntry indexKeyData) {
+ if (valueData.getSize() == 0) {
+ return false;
+ }
+ if (valueData.getSize() != 1) {
+ throw new IllegalStateException();
+ }
+ byte val = valueData.getData()[valueData.getOffset()];
+ if (val == 0) {
+ return false; // fixed-len pad value
+ }
+ val -= 100;
+ if (isRecNum) {
+ RecordNumberBinding.recordNumberToEntry(val, indexKeyData);
+ } else {
+ indexKeyData.setData(new byte[] { val }, 0, 1);
+ }
+ return true;
+ }
+
+ public void clearIndexKey(DatabaseEntry valueData) {
+
+ throw new RuntimeException("not supported");
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TestSR15721.java b/test/scr024/src/com/sleepycat/collections/test/TestSR15721.java
new file mode 100644
index 00000000..749e3754
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TestSR15721.java
@@ -0,0 +1,119 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.collections.CurrentTransaction;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Chao Huang
+ */
+public class TestSR15721 extends TestCase {
+
+ /**
+ * Runs a command line collection test.
+ * @see #usage
+ */
+ public static void main(String[] args) {
+ if (args.length == 1 &&
+ (args[0].equals("-h") || args[0].equals("-help"))) {
+ usage();
+ } else {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+ }
+
+ private static void usage() {
+
+ System.out.println(
+ "Usage: java com.sleepycat.collections.test.TestSR15721"
+ + " [-h | -help]\n");
+ System.exit(2);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(TestSR15721.class);
+ return suite;
+ }
+
+ private Environment env;
+ private CurrentTransaction currentTxn;
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ env = TestEnv.TXN.open("TestSR15721");
+ currentTxn = CurrentTransaction.getInstance(env);
+ }
+
+ @Override
+ public void tearDown() {
+ try {
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ env = null;
+ currentTxn = null;
+ }
+ }
+
+ /**
+ * Tests that the CurrentTransaction instance doesn't indeed allow GC to
+ * reclaim while attached environment is open. [#15721]
+ */
+ public void testSR15721Fix()
+ throws Exception {
+
+ int hash = currentTxn.hashCode();
+ int hash2 = -1;
+
+ currentTxn = CurrentTransaction.getInstance(env);
+ hash2 = currentTxn.hashCode();
+ assertTrue(hash == hash2);
+
+ currentTxn.beginTransaction(null);
+ currentTxn = null;
+ hash2 = -1;
+
+ for (int i = 0; i < 10; i += 1) {
+ byte[] x = null;
+ try {
+ x = new byte[Integer.MAX_VALUE - 1];
+ fail();
+ } catch (OutOfMemoryError expected) {
+ }
+ assertNull(x);
+
+ System.gc();
+ }
+
+ currentTxn = CurrentTransaction.getInstance(env);
+ hash2 = currentTxn.hashCode();
+ currentTxn.commitTransaction();
+
+ assertTrue(hash == hash2);
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TestStore.java b/test/scr024/src/com/sleepycat/collections/test/TestStore.java
new file mode 100644
index 00000000..f7191939
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TestStore.java
@@ -0,0 +1,279 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.collections.CurrentTransaction;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.SecondaryConfig;
+
+/**
+ * @author Mark Hayes
+ */
+class TestStore {
+
+ static final TestKeyCreator BYTE_EXTRACTOR = new TestKeyCreator(false);
+ static final TestKeyCreator RECNO_EXTRACTOR = new TestKeyCreator(true);
+ static final EntryBinding VALUE_BINDING = new TestDataBinding();
+ static final EntryBinding BYTE_KEY_BINDING = VALUE_BINDING;
+ static final EntryBinding RECNO_KEY_BINDING = new RecordNumberBinding();
+ static final EntityBinding BYTE_ENTITY_BINDING =
+ new TestEntityBinding(false);
+ static final EntityBinding RECNO_ENTITY_BINDING =
+ new TestEntityBinding(true);
+ static final TestKeyAssigner BYTE_KEY_ASSIGNER =
+ new TestKeyAssigner(false);
+ static final TestKeyAssigner RECNO_KEY_ASSIGNER =
+ new TestKeyAssigner(true);
+
+ static final TestStore BTREE_UNIQ;
+ static final TestStore BTREE_DUP;
+ static final TestStore BTREE_DUPSORT;
+ static final TestStore BTREE_RECNUM;
+ static final TestStore HASH_UNIQ;
+ static final TestStore HASH_DUP;
+ static final TestStore HASH_DUPSORT;
+ static final TestStore QUEUE;
+ static final TestStore RECNO;
+ static final TestStore RECNO_RENUM;
+
+ static final TestStore[] ALL;
+ static {
+ List list = new ArrayList();
+ SecondaryConfig config;
+
+ config = new SecondaryConfig();
+ DbCompat.setTypeBtree(config);
+ BTREE_UNIQ = new TestStore("btree-uniq", config);
+ BTREE_UNIQ.indexOf = BTREE_UNIQ;
+ list.add(BTREE_UNIQ);
+
+ if (DbCompat.INSERTION_ORDERED_DUPLICATES) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeBtree(config);
+ DbCompat.setUnsortedDuplicates(config, true);
+ BTREE_DUP = new TestStore("btree-dup", config);
+ BTREE_DUP.indexOf = null; // indexes must use sorted dups
+ list.add(BTREE_DUP);
+ } else {
+ BTREE_DUP = null;
+ }
+
+ config = new SecondaryConfig();
+ DbCompat.setTypeBtree(config);
+ DbCompat.setSortedDuplicates(config, true);
+ BTREE_DUPSORT = new TestStore("btree-dupsort", config);
+ BTREE_DUPSORT.indexOf = BTREE_UNIQ;
+ list.add(BTREE_DUPSORT);
+
+ if (DbCompat.BTREE_RECNUM_METHOD) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeBtree(config);
+ DbCompat.setBtreeRecordNumbers(config, true);
+ BTREE_RECNUM = new TestStore("btree-recnum", config);
+ BTREE_RECNUM.indexOf = BTREE_RECNUM;
+ list.add(BTREE_RECNUM);
+ } else {
+ BTREE_RECNUM = null;
+ }
+
+ if (DbCompat.HASH_METHOD) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeHash(config);
+ HASH_UNIQ = new TestStore("hash-uniq", config);
+ HASH_UNIQ.indexOf = HASH_UNIQ;
+ list.add(HASH_UNIQ);
+
+ if (DbCompat.INSERTION_ORDERED_DUPLICATES) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeHash(config);
+ DbCompat.setUnsortedDuplicates(config, true);
+ HASH_DUP = new TestStore("hash-dup", config);
+ HASH_DUP.indexOf = null; // indexes must use sorted dups
+ list.add(HASH_DUP);
+ } else {
+ HASH_DUP = null;
+ }
+
+ config = new SecondaryConfig();
+ DbCompat.setTypeHash(config);
+ DbCompat.setSortedDuplicates(config, true);
+ HASH_DUPSORT = new TestStore("hash-dupsort", config);
+ HASH_DUPSORT.indexOf = HASH_UNIQ;
+ list.add(HASH_DUPSORT);
+ } else {
+ HASH_UNIQ = null;
+ HASH_DUP = null;
+ HASH_DUPSORT = null;
+ }
+
+ if (DbCompat.QUEUE_METHOD) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeQueue(config);
+ QUEUE = new TestStore("queue", config);
+ QUEUE.indexOf = QUEUE;
+ list.add(QUEUE);
+ } else {
+ QUEUE = null;
+ }
+
+ if (DbCompat.RECNO_METHOD) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeRecno(config);
+ RECNO = new TestStore("recno", config);
+ RECNO.indexOf = RECNO;
+ list.add(RECNO);
+
+ config = new SecondaryConfig();
+ DbCompat.setTypeRecno(config);
+ DbCompat.setRenumbering(config, true);
+ RECNO_RENUM = new TestStore("recno-renum", config);
+ RECNO_RENUM.indexOf = null; // indexes must have stable keys
+ list.add(RECNO_RENUM);
+ } else {
+ RECNO = null;
+ RECNO_RENUM = null;
+ }
+
+ ALL = new TestStore[list.size()];
+ list.toArray(ALL);
+ }
+
+ private String name;
+ private SecondaryConfig config;
+ private TestStore indexOf;
+ private boolean isRecNumFormat;
+
+ private TestStore(String name, SecondaryConfig config) {
+
+ this.name = name;
+ this.config = config;
+
+ isRecNumFormat = isQueueOrRecno() ||
+ (DbCompat.isTypeBtree(config) &&
+ DbCompat.getBtreeRecordNumbers(config));
+ }
+
+ EntryBinding getValueBinding() {
+
+ return VALUE_BINDING;
+ }
+
+ EntryBinding getKeyBinding() {
+
+ return isRecNumFormat ? RECNO_KEY_BINDING : BYTE_KEY_BINDING;
+ }
+
+ EntityBinding getEntityBinding() {
+
+ return isRecNumFormat ? RECNO_ENTITY_BINDING : BYTE_ENTITY_BINDING;
+ }
+
+ TestKeyAssigner getKeyAssigner() {
+
+ if (isQueueOrRecno()) {
+ return null;
+ } else {
+ if (isRecNumFormat) {
+ return RECNO_KEY_ASSIGNER;
+ } else {
+ return BYTE_KEY_ASSIGNER;
+ }
+ }
+ }
+
+ String getName() {
+
+ return name;
+ }
+
+ boolean isOrdered() {
+
+ return !DbCompat.isTypeHash(config);
+ }
+
+ boolean isQueueOrRecno() {
+
+ return DbCompat.isTypeQueue(config) || DbCompat.isTypeRecno(config);
+ }
+
+ boolean areKeyRangesAllowed() {
+ return isOrdered() && !isQueueOrRecno();
+ }
+
+ boolean areDuplicatesAllowed() {
+
+ return DbCompat.getSortedDuplicates(config) ||
+ DbCompat.getUnsortedDuplicates(config);
+ }
+
+ boolean hasRecNumAccess() {
+
+ return isRecNumFormat;
+ }
+
+ boolean areKeysRenumbered() {
+
+ return hasRecNumAccess() &&
+ (DbCompat.isTypeBtree(config) ||
+ DbCompat.getRenumbering(config));
+ }
+
+ TestStore getIndexOf() {
+
+ return DbCompat.SECONDARIES ? indexOf : null;
+ }
+
+ Database open(Environment env, String fileName)
+ throws DatabaseException {
+
+ int fixedLen = (isQueueOrRecno() ? 1 : 0);
+ return openDb(env, fileName, fixedLen, null);
+ }
+
+ Database openIndex(Database primary, String fileName)
+ throws DatabaseException {
+
+ int fixedLen = (isQueueOrRecno() ? 4 : 0);
+ config.setKeyCreator(isRecNumFormat ? RECNO_EXTRACTOR
+ : BYTE_EXTRACTOR);
+ Environment env = primary.getEnvironment();
+ return openDb(env, fileName, fixedLen, primary);
+ }
+
+ private Database openDb(Environment env, String fileName, int fixedLen,
+ Database primary)
+ throws DatabaseException {
+
+ if (fixedLen > 0) {
+ DbCompat.setRecordLength(config, fixedLen);
+ DbCompat.setRecordPad(config, 0);
+ } else {
+ DbCompat.setRecordLength(config, 0);
+ }
+ config.setAllowCreate(true);
+ DbCompat.setReadUncommitted(config, true);
+ config.setTransactional(CurrentTransaction.getInstance(env) != null);
+ if (primary != null) {
+ return DbCompat.testOpenSecondaryDatabase
+ (env, null, fileName, null, primary, config);
+ } else {
+ return DbCompat.testOpenDatabase
+ (env, null, fileName, null, config);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/TransactionTest.java b/test/scr024/src/com/sleepycat/collections/test/TransactionTest.java
new file mode 100644
index 00000000..a427f2ae
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/TransactionTest.java
@@ -0,0 +1,838 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.collections.CurrentTransaction;
+import com.sleepycat.collections.StoredCollections;
+import com.sleepycat.collections.StoredContainer;
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.StoredList;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+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.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.db.TransactionConfig;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class TransactionTest extends TestCase {
+
+ private static final Long ONE = new Long(1);
+ private static final Long TWO = new Long(2);
+ private static final Long THREE = new Long(3);
+
+ /**
+ * Runs a command line collection test.
+ * @see #usage
+ */
+ public static void main(String[] args) {
+ if (args.length == 1 &&
+ (args[0].equals("-h") || args[0].equals("-help"))) {
+ usage();
+ } else {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+ }
+
+ private static void usage() {
+
+ System.out.println(
+ "Usage: java com.sleepycat.collections.test.TransactionTest"
+ + " [-h | -help]\n");
+ System.exit(2);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(TransactionTest.class);
+ return suite;
+ }
+
+ private Environment env;
+ private CurrentTransaction currentTxn;
+ private Database store;
+ private StoredSortedMap map;
+ private TestStore testStore = TestStore.BTREE_UNIQ;
+
+ public TransactionTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(SharedTestUtils.qualifiedTestName(this));
+ env = TestEnv.TXN.open("TransactionTests");
+ currentTxn = CurrentTransaction.getInstance(env);
+ store = testStore.open(env, dbName(0));
+ map = new StoredSortedMap(store, testStore.getKeyBinding(),
+ testStore.getValueBinding(), true);
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (store != null) {
+ store.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ store = null;
+ env = null;
+ currentTxn = null;
+ map = null;
+ testStore = null;
+ }
+ }
+
+ private String dbName(int i) {
+
+ return "txn-test-" + getName() + '-' + i;
+ }
+
+ public void testGetters()
+ throws Exception {
+
+ assertNotNull(env);
+ assertNotNull(currentTxn);
+ assertNull(currentTxn.getTransaction());
+
+ currentTxn.beginTransaction(null);
+ assertNotNull(currentTxn.getTransaction());
+ currentTxn.commitTransaction();
+ assertNull(currentTxn.getTransaction());
+
+ currentTxn.beginTransaction(null);
+ assertNotNull(currentTxn.getTransaction());
+ currentTxn.abortTransaction();
+ assertNull(currentTxn.getTransaction());
+
+ // read-uncommitted property should be inherited
+
+ assertTrue(!isReadUncommitted(map));
+ assertTrue(!isReadUncommitted(map.values()));
+ assertTrue(!isReadUncommitted(map.keySet()));
+ assertTrue(!isReadUncommitted(map.entrySet()));
+
+ StoredSortedMap other = (StoredSortedMap)
+ StoredCollections.configuredMap
+ (map, CursorConfig.READ_UNCOMMITTED);
+ assertTrue(isReadUncommitted(other));
+ assertTrue(isReadUncommitted(other.values()));
+ assertTrue(isReadUncommitted(other.keySet()));
+ assertTrue(isReadUncommitted(other.entrySet()));
+ assertTrue(!isReadUncommitted(map));
+ assertTrue(!isReadUncommitted(map.values()));
+ assertTrue(!isReadUncommitted(map.keySet()));
+ assertTrue(!isReadUncommitted(map.entrySet()));
+
+ // read-committed property should be inherited
+
+ assertTrue(!isReadCommitted(map));
+ assertTrue(!isReadCommitted(map.values()));
+ assertTrue(!isReadCommitted(map.keySet()));
+ assertTrue(!isReadCommitted(map.entrySet()));
+
+ other = (StoredSortedMap)
+ StoredCollections.configuredMap
+ (map, CursorConfig.READ_COMMITTED);
+ assertTrue(isReadCommitted(other));
+ assertTrue(isReadCommitted(other.values()));
+ assertTrue(isReadCommitted(other.keySet()));
+ assertTrue(isReadCommitted(other.entrySet()));
+ assertTrue(!isReadCommitted(map));
+ assertTrue(!isReadCommitted(map.values()));
+ assertTrue(!isReadCommitted(map.keySet()));
+ assertTrue(!isReadCommitted(map.entrySet()));
+ }
+
+ public void testTransactional()
+ throws Exception {
+
+ // is transactional because DB_AUTO_COMMIT was passed to
+ // Database.open()
+ //
+ assertTrue(map.isTransactional());
+ store.close();
+ store = null;
+
+ // is not transactional
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ DbCompat.setTypeBtree(dbConfig);
+ dbConfig.setAllowCreate(true);
+ Database db = DbCompat.testOpenDatabase
+ (env, null, dbName(1), null, dbConfig);
+ map = new StoredSortedMap(db, testStore.getKeyBinding(),
+ testStore.getValueBinding(), true);
+ assertTrue(!map.isTransactional());
+ map.put(ONE, ONE);
+ readCheck(map, ONE, ONE);
+ db.close();
+
+ // is transactional
+ //
+ dbConfig.setTransactional(true);
+ currentTxn.beginTransaction(null);
+ db = DbCompat.testOpenDatabase
+ (env, currentTxn.getTransaction(), dbName(2), null, dbConfig);
+ currentTxn.commitTransaction();
+ map = new StoredSortedMap(db, testStore.getKeyBinding(),
+ testStore.getValueBinding(), true);
+ assertTrue(map.isTransactional());
+ currentTxn.beginTransaction(null);
+ map.put(ONE, ONE);
+ readCheck(map, ONE, ONE);
+ currentTxn.commitTransaction();
+ db.close();
+ }
+
+ public void testExceptions()
+ throws Exception {
+
+ try {
+ currentTxn.commitTransaction();
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ try {
+ currentTxn.abortTransaction();
+ fail();
+ } catch (IllegalStateException expected) {}
+ }
+
+ public void testNested()
+ throws Exception {
+
+ if (!DbCompat.NESTED_TRANSACTIONS) {
+ return;
+ }
+ assertNull(currentTxn.getTransaction());
+
+ Transaction txn1 = currentTxn.beginTransaction(null);
+ assertNotNull(txn1);
+ assertTrue(txn1 == currentTxn.getTransaction());
+
+ assertNull(map.get(ONE));
+ assertNull(map.put(ONE, ONE));
+ assertEquals(ONE, map.get(ONE));
+
+ Transaction txn2 = currentTxn.beginTransaction(null);
+ assertNotNull(txn2);
+ assertTrue(txn2 == currentTxn.getTransaction());
+ assertTrue(txn1 != txn2);
+
+ assertNull(map.put(TWO, TWO));
+ assertEquals(TWO, map.get(TWO));
+
+ Transaction txn3 = currentTxn.beginTransaction(null);
+ assertNotNull(txn3);
+ assertTrue(txn3 == currentTxn.getTransaction());
+ assertTrue(txn1 != txn2);
+ assertTrue(txn1 != txn3);
+ assertTrue(txn2 != txn3);
+
+ assertNull(map.put(THREE, THREE));
+ assertEquals(THREE, map.get(THREE));
+
+ Transaction txn = currentTxn.abortTransaction();
+ assertTrue(txn == txn2);
+ assertTrue(txn == currentTxn.getTransaction());
+ assertNull(map.get(THREE));
+ assertEquals(TWO, map.get(TWO));
+
+ txn3 = currentTxn.beginTransaction(null);
+ assertNotNull(txn3);
+ assertTrue(txn3 == currentTxn.getTransaction());
+ assertTrue(txn1 != txn2);
+ assertTrue(txn1 != txn3);
+ assertTrue(txn2 != txn3);
+
+ assertNull(map.put(THREE, THREE));
+ assertEquals(THREE, map.get(THREE));
+
+ txn = currentTxn.commitTransaction();
+ assertTrue(txn == txn2);
+ assertTrue(txn == currentTxn.getTransaction());
+ assertEquals(THREE, map.get(THREE));
+ assertEquals(TWO, map.get(TWO));
+
+ txn = currentTxn.commitTransaction();
+ assertTrue(txn == txn1);
+ assertTrue(txn == currentTxn.getTransaction());
+ assertEquals(THREE, map.get(THREE));
+ assertEquals(TWO, map.get(TWO));
+ assertEquals(ONE, map.get(ONE));
+
+ txn = currentTxn.commitTransaction();
+ assertNull(txn);
+ assertNull(currentTxn.getTransaction());
+ assertEquals(THREE, map.get(THREE));
+ assertEquals(TWO, map.get(TWO));
+ assertEquals(ONE, map.get(ONE));
+ }
+
+ public void testRunnerCommit()
+ throws Exception {
+
+ commitTest(false);
+ }
+
+ public void testExplicitCommit()
+ throws Exception {
+
+ commitTest(true);
+ }
+
+ private void commitTest(final boolean explicit)
+ throws Exception {
+
+ final TransactionRunner runner = new TransactionRunner(env);
+ runner.setAllowNestedTransactions(DbCompat.NESTED_TRANSACTIONS);
+
+ assertNull(currentTxn.getTransaction());
+
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ final Transaction txn1 = currentTxn.getTransaction();
+ assertNotNull(txn1);
+ assertNull(map.put(ONE, ONE));
+ assertEquals(ONE, map.get(ONE));
+
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ final Transaction txn2 = currentTxn.getTransaction();
+ assertNotNull(txn2);
+ if (DbCompat.NESTED_TRANSACTIONS) {
+ assertTrue(txn1 != txn2);
+ } else {
+ assertTrue(txn1 == txn2);
+ }
+ assertNull(map.put(TWO, TWO));
+ assertEquals(TWO, map.get(TWO));
+ assertEquals(ONE, map.get(ONE));
+ if (DbCompat.NESTED_TRANSACTIONS && explicit) {
+ currentTxn.commitTransaction();
+ }
+ }
+ });
+
+ Transaction txn3 = currentTxn.getTransaction();
+ assertSame(txn1, txn3);
+
+ assertEquals(TWO, map.get(TWO));
+ assertEquals(ONE, map.get(ONE));
+ }
+ });
+
+ assertNull(currentTxn.getTransaction());
+ }
+
+ public void testRunnerAbort()
+ throws Exception {
+
+ abortTest(false);
+ }
+
+ public void testExplicitAbort()
+ throws Exception {
+
+ abortTest(true);
+ }
+
+ private void abortTest(final boolean explicit)
+ throws Exception {
+
+ final TransactionRunner runner = new TransactionRunner(env);
+ runner.setAllowNestedTransactions(DbCompat.NESTED_TRANSACTIONS);
+
+ assertNull(currentTxn.getTransaction());
+
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ final Transaction txn1 = currentTxn.getTransaction();
+ assertNotNull(txn1);
+ assertNull(map.put(ONE, ONE));
+ assertEquals(ONE, map.get(ONE));
+
+ if (DbCompat.NESTED_TRANSACTIONS) {
+ try {
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ final Transaction txn2 =
+ currentTxn.getTransaction();
+ assertNotNull(txn2);
+ assertTrue(txn1 != txn2);
+ assertNull(map.put(TWO, TWO));
+ assertEquals(TWO, map.get(TWO));
+ if (explicit) {
+ currentTxn.abortTransaction();
+ } else {
+ throw new IllegalArgumentException(
+ "test-abort");
+ }
+ }
+ });
+ assertTrue(explicit);
+ } catch (IllegalArgumentException e) {
+ assertTrue(!explicit);
+ assertEquals("test-abort", e.getMessage());
+ }
+ }
+
+ Transaction txn3 = currentTxn.getTransaction();
+ assertSame(txn1, txn3);
+
+ assertEquals(ONE, map.get(ONE));
+ assertNull(map.get(TWO));
+ }
+ });
+
+ assertNull(currentTxn.getTransaction());
+ }
+
+ public void testReadCommittedCollection()
+ throws Exception {
+
+ StoredSortedMap degree2Map = (StoredSortedMap)
+ StoredCollections.configuredSortedMap
+ (map, CursorConfig.READ_COMMITTED);
+
+ // original map is not read-committed
+ assertTrue(!isReadCommitted(map));
+
+ // all read-committed containers are read-uncommitted
+ assertTrue(isReadCommitted(degree2Map));
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredMap
+ (map, CursorConfig.READ_COMMITTED)));
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredCollection
+ (map.values(), CursorConfig.READ_COMMITTED)));
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredSet
+ (map.keySet(), CursorConfig.READ_COMMITTED)));
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredSortedSet
+ ((SortedSet) map.keySet(),
+ CursorConfig.READ_COMMITTED)));
+
+ if (DbCompat.RECNO_METHOD) {
+ // create a list just so we can call configuredList()
+ Database listStore = TestStore.RECNO_RENUM.open(env, "foo");
+ List list = new StoredList(listStore, TestStore.VALUE_BINDING,
+ true);
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredList
+ (list, CursorConfig.READ_COMMITTED)));
+ listStore.close();
+ }
+
+ map.put(ONE, ONE);
+ doReadCommitted(degree2Map, null);
+ }
+
+ private static boolean isReadCommitted(Object container) {
+ StoredContainer storedContainer = (StoredContainer) container;
+ /* We can't use getReadCommitted until is is added to DB core. */
+ return storedContainer.getCursorConfig() != null &&
+ storedContainer.getCursorConfig().getReadCommitted();
+ }
+
+ public void testReadCommittedTransaction()
+ throws Exception {
+
+ TransactionConfig config = new TransactionConfig();
+ config.setReadCommitted(true);
+ doReadCommitted(map, config);
+ }
+
+ private void doReadCommitted(final StoredSortedMap degree2Map,
+ TransactionConfig txnConfig)
+ throws Exception {
+
+ map.put(ONE, ONE);
+ TransactionRunner runner = new TransactionRunner(env);
+ runner.setTransactionConfig(txnConfig);
+ assertNull(currentTxn.getTransaction());
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ assertNotNull(currentTxn.getTransaction());
+
+ /* Do a read-committed get(), the lock is not retained. */
+ assertEquals(ONE, degree2Map.get(ONE));
+
+ /*
+ * If we were not using read-committed, the following write of
+ * key ONE with an auto-commit transaction would self-deadlock
+ * since two transactions in the same thread would be
+ * attempting to lock the same key, one for write and one for
+ * read. This test passes if we do not deadlock.
+ */
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ testStore.getKeyBinding().objectToEntry(ONE, key);
+ testStore.getValueBinding().objectToEntry(TWO, value);
+ store.put(null, key, value);
+ }
+ });
+ assertNull(currentTxn.getTransaction());
+ }
+
+ public void testReadUncommittedCollection()
+ throws Exception {
+
+ StoredSortedMap dirtyMap = (StoredSortedMap)
+ StoredCollections.configuredSortedMap
+ (map, CursorConfig.READ_UNCOMMITTED);
+
+ // original map is not read-uncommitted
+ assertTrue(!isReadUncommitted(map));
+
+ // all read-uncommitted containers are read-uncommitted
+ assertTrue(isReadUncommitted(dirtyMap));
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredMap
+ (map, CursorConfig.READ_UNCOMMITTED)));
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredCollection
+ (map.values(), CursorConfig.READ_UNCOMMITTED)));
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredSet
+ (map.keySet(), CursorConfig.READ_UNCOMMITTED)));
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredSortedSet
+ ((SortedSet) map.keySet(), CursorConfig.READ_UNCOMMITTED)));
+
+ if (DbCompat.RECNO_METHOD) {
+ // create a list just so we can call configuredList()
+ Database listStore = TestStore.RECNO_RENUM.open(env, "foo");
+ List list = new StoredList(listStore, TestStore.VALUE_BINDING,
+ true);
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredList
+ (list, CursorConfig.READ_UNCOMMITTED)));
+ listStore.close();
+ }
+
+ doReadUncommitted(dirtyMap);
+ }
+
+ private static boolean isReadUncommitted(Object container) {
+ StoredContainer storedContainer = (StoredContainer) container;
+ return storedContainer.getCursorConfig() != null &&
+ storedContainer.getCursorConfig().getReadUncommitted();
+ }
+
+ public void testReadUncommittedTransaction()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(env);
+ TransactionConfig config = new TransactionConfig();
+ config.setReadUncommitted(true);
+ runner.setTransactionConfig(config);
+ assertNull(currentTxn.getTransaction());
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ assertNotNull(currentTxn.getTransaction());
+ doReadUncommitted(map);
+ }
+ });
+ assertNull(currentTxn.getTransaction());
+ }
+
+ /**
+ * Tests that the CurrentTransaction static WeakHashMap does indeed allow
+ * GC to reclaim tine environment when it is closed. At one point this was
+ * not working because the value object in the map has a reference to the
+ * environment. This was fixed by wrapping the Environment in a
+ * WeakReference. [#15444]
+ *
+ * This test only succeeds intermittently, probably due to its reliance
+ * on the GC call.
+ */
+ public void testCurrentTransactionGC()
+ throws Exception {
+
+ /*
+ * This test can have indeterminate results because it depends on
+ * a finalize count, so it's not part of the default run.
+ */
+ if (!SharedTestUtils.runLongTests()) {
+ return;
+ }
+
+ final StringBuffer finalizedFlag = new StringBuffer();
+
+ class MyEnv extends Environment {
+
+ /**
+ * @throws FileNotFoundException from DB core.
+ */
+ MyEnv(File home, EnvironmentConfig config)
+ throws DatabaseException, FileNotFoundException {
+
+ super(home, config);
+ }
+
+ @Override
+ protected void finalize() {
+ finalizedFlag.append('.');
+ }
+ }
+
+ MyEnv myEnv = new MyEnv(env.getHome(), env.getConfig());
+ CurrentTransaction myCurrTxn = CurrentTransaction.getInstance(myEnv);
+
+ store.close();
+ store = null;
+ map = null;
+
+ env.close();
+ env = null;
+
+ myEnv.close();
+ myEnv = null;
+
+ myCurrTxn = null;
+ currentTxn = null;
+
+ for (int i = 0; i < 10; i += 1) {
+ byte[] x = null;
+ try {
+ x = new byte[Integer.MAX_VALUE - 1];
+ } catch (OutOfMemoryError expected) {
+ }
+ assertNull(x);
+ System.gc();
+ }
+
+ for (int i = 0; i < 10; i += 1) {
+ System.gc();
+ }
+
+ assertTrue(finalizedFlag.length() > 0);
+ }
+
+ private synchronized void doReadUncommitted(StoredSortedMap dirtyMap)
+ throws Exception {
+
+ // start thread one
+ ReadUncommittedThreadOne t1 = new ReadUncommittedThreadOne(env, this);
+ t1.start();
+ wait();
+
+ // put ONE
+ synchronized (t1) { t1.notify(); }
+ wait();
+ readCheck(dirtyMap, ONE, ONE);
+ assertTrue(!dirtyMap.isEmpty());
+
+ // abort ONE
+ synchronized (t1) { t1.notify(); }
+ t1.join();
+ readCheck(dirtyMap, ONE, null);
+ assertTrue(dirtyMap.isEmpty());
+
+ // start thread two
+ ReadUncommittedThreadTwo t2 = new ReadUncommittedThreadTwo(env, this);
+ t2.start();
+ wait();
+
+ // put TWO
+ synchronized (t2) { t2.notify(); }
+ wait();
+ readCheck(dirtyMap, TWO, TWO);
+ assertTrue(!dirtyMap.isEmpty());
+
+ // commit TWO
+ synchronized (t2) { t2.notify(); }
+ t2.join();
+ readCheck(dirtyMap, TWO, TWO);
+ assertTrue(!dirtyMap.isEmpty());
+ }
+
+ private static class ReadUncommittedThreadOne extends Thread {
+
+ private final CurrentTransaction currentTxn;
+ private final TransactionTest parent;
+ private final StoredSortedMap map;
+
+ private ReadUncommittedThreadOne(Environment env,
+ TransactionTest parent) {
+
+ this.currentTxn = CurrentTransaction.getInstance(env);
+ this.parent = parent;
+ this.map = parent.map;
+ }
+
+ @Override
+ public synchronized void run() {
+
+ try {
+ assertNull(currentTxn.getTransaction());
+ assertNotNull(currentTxn.beginTransaction(null));
+ assertNotNull(currentTxn.getTransaction());
+ readCheck(map, ONE, null);
+ synchronized (parent) { parent.notify(); }
+ wait();
+
+ // put ONE
+ assertNull(map.put(ONE, ONE));
+ readCheck(map, ONE, ONE);
+ synchronized (parent) { parent.notify(); }
+ wait();
+
+ // abort ONE
+ assertNull(currentTxn.abortTransaction());
+ assertNull(currentTxn.getTransaction());
+ } catch (Exception e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+ }
+
+ private static class ReadUncommittedThreadTwo extends Thread {
+
+ private final Environment env;
+ private final CurrentTransaction currentTxn;
+ private final TransactionTest parent;
+ private final StoredSortedMap map;
+
+ private ReadUncommittedThreadTwo(Environment env,
+ TransactionTest parent) {
+
+ this.env = env;
+ this.currentTxn = CurrentTransaction.getInstance(env);
+ this.parent = parent;
+ this.map = parent.map;
+ }
+
+ @Override
+ public synchronized void run() {
+
+ try {
+ final TransactionRunner runner = new TransactionRunner(env);
+ final Object thread = this;
+ assertNull(currentTxn.getTransaction());
+
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ assertNotNull(currentTxn.getTransaction());
+ readCheck(map, TWO, null);
+ synchronized (parent) { parent.notify(); }
+ thread.wait();
+
+ // put TWO
+ assertNull(map.put(TWO, TWO));
+ readCheck(map, TWO, TWO);
+ synchronized (parent) { parent.notify(); }
+ thread.wait();
+
+ // commit TWO
+ }
+ });
+ assertNull(currentTxn.getTransaction());
+ } catch (Exception e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+ }
+
+ private static void readCheck(StoredSortedMap checkMap, Object key,
+ Object expect) {
+ if (expect == null) {
+ assertNull(checkMap.get(key));
+ assertTrue(checkMap.tailMap(key).isEmpty());
+ assertTrue(!checkMap.tailMap(key).containsKey(key));
+ assertTrue(!checkMap.keySet().contains(key));
+ assertTrue(checkMap.duplicates(key).isEmpty());
+ Iterator i = checkMap.keySet().iterator();
+ try {
+ while (i.hasNext()) {
+ assertTrue(!key.equals(i.next()));
+ }
+ } finally { StoredIterator.close(i); }
+ } else {
+ assertEquals(expect, checkMap.get(key));
+ assertEquals(expect, checkMap.tailMap(key).get(key));
+ assertTrue(!checkMap.tailMap(key).isEmpty());
+ assertTrue(checkMap.tailMap(key).containsKey(key));
+ assertTrue(checkMap.keySet().contains(key));
+ assertTrue(checkMap.values().contains(expect));
+ assertTrue(!checkMap.duplicates(key).isEmpty());
+ assertTrue(checkMap.duplicates(key).contains(expect));
+ Iterator i = checkMap.keySet().iterator();
+ try {
+ boolean found = false;
+ while (i.hasNext()) {
+ if (expect.equals(i.next())) {
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally { StoredIterator.close(i); }
+ }
+ }
+
+ /**
+ * Tests transaction retries performed by TransationRunner.
+ *
+ * This test is too sensitive to how lock conflict detection works on JE to
+ * make it work properly on DB core.
+ */
+
+ /**
+ * Tests transaction retries performed by TransationRunner.
+ *
+ * This test is too sensitive to how lock conflict detection works on JE to
+ * make it work properly on DB core.
+ */
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/serial/CatalogCornerCaseTest.java b/test/scr024/src/com/sleepycat/collections/test/serial/CatalogCornerCaseTest.java
new file mode 100644
index 00000000..797a0bbf
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/serial/CatalogCornerCaseTest.java
@@ -0,0 +1,97 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class CatalogCornerCaseTest extends TestCase {
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ return new TestSuite(CatalogCornerCaseTest.class);
+ }
+
+ private Environment env;
+
+ public CatalogCornerCaseTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = TestEnv.BDB.open(getName());
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ env = null;
+ }
+ }
+
+ public void testReadOnlyEmptyCatalog()
+ throws Exception {
+
+ String file = "catalog.db";
+
+ /* Create an empty database. */
+ DatabaseConfig config = new DatabaseConfig();
+ config.setAllowCreate(true);
+ DbCompat.setTypeBtree(config);
+ Database db =
+ DbCompat.testOpenDatabase(env, null, file, null, config);
+ db.close();
+
+ /* Open the empty database read-only. */
+ config.setAllowCreate(false);
+ config.setReadOnly(true);
+ db = DbCompat.testOpenDatabase(env, null, file, null, config);
+
+ /* Expect exception when creating the catalog. */
+ try {
+ new StoredClassCatalog(db);
+ fail();
+ } catch (IllegalStateException e) { }
+ db.close();
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTest.java b/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTest.java
new file mode 100644
index 00000000..f7a8c3f5
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTest.java
@@ -0,0 +1,177 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+import java.io.ObjectStreamClass;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Runs part two of the StoredClassCatalogTest. This part is run with the
+ * new/updated version of TestSerial in the classpath. It uses the
+ * environment and databases created by StoredClassCatalogTestInit. It
+ * verifies that it can read objects serialized using the old class format,
+ * and that it can create new objects with the new class format.
+ *
+ * @author Mark Hayes
+ */
+public class StoredClassCatalogTest extends TestCase
+ implements TransactionWorker {
+
+ static final String CATALOG_FILE = "catalogtest-catalog.db";
+ static final String STORE_FILE = "catalogtest-store.db";
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ suite.addTest(new StoredClassCatalogTest(TestEnv.ALL[i]));
+ }
+ return suite;
+ }
+
+ private TestEnv testEnv;
+ private Environment env;
+ private StoredClassCatalog catalog;
+ private StoredClassCatalog catalog2;
+ private Database store;
+ private Map map;
+ private TransactionRunner runner;
+
+ public StoredClassCatalogTest(TestEnv testEnv) {
+
+ super(makeTestName(testEnv));
+ this.testEnv = testEnv;
+ }
+
+ static String makeTestName(TestEnv testEnv) {
+ return "StoredClassCatalogTest-" + testEnv.getName();
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = testEnv.open(makeTestName(testEnv), false);
+ runner = new TransactionRunner(env);
+
+ catalog = new StoredClassCatalog(openDb(CATALOG_FILE, false));
+ catalog2 = new StoredClassCatalog(openDb("catalog2.db", true));
+
+ SerialBinding keyBinding = new SerialBinding(catalog,
+ String.class);
+ SerialBinding valueBinding = new SerialBinding(catalog,
+ TestSerial.class);
+ store = openDb(STORE_FILE, false);
+
+ map = new StoredMap(store, keyBinding, valueBinding, true);
+ }
+
+ private Database openDb(String file, boolean create)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(testEnv.isTxnMode());
+ config.setAllowCreate(create);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (catalog != null) {
+ catalog.close();
+ catalog.close(); // should have no effect
+ }
+ if (catalog2 != null) {
+ catalog2.close();
+ }
+ if (store != null) {
+ store.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.err.println("Ignored exception during tearDown: ");
+ e.printStackTrace();
+ } finally {
+ /* Ensure that GC can cleanup. */
+ catalog = null;
+ catalog2 = null;
+ store = null;
+ env = null;
+ testEnv = null;
+ map = null;
+ runner = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ runner.run(this);
+ }
+
+ public void doWork()
+ throws Exception {
+
+ TestSerial one = (TestSerial) map.get("one");
+ TestSerial two = (TestSerial) map.get("two");
+ assertNotNull(one);
+ assertNotNull(two);
+ assertEquals(one, two.getOther());
+ assertNull(one.getStringField());
+ assertNull(two.getStringField());
+
+ TestSerial three = new TestSerial(two);
+ assertNotNull(three.getStringField());
+ map.put("three", three);
+ three = (TestSerial) map.get("three");
+ assertEquals(two, three.getOther());
+
+ ObjectStreamClass desc = ObjectStreamClass.lookup(TestSerial.class);
+
+ assertNotNull(catalog.getClassID(desc));
+ assertNotNull(catalog.getClassID(desc));
+
+ // test with empty catalog
+ assertNotNull(catalog2.getClassID(desc));
+ assertNotNull(catalog2.getClassID(desc));
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTestInit.java b/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTestInit.java
new file mode 100644
index 00000000..96ad345a
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTestInit.java
@@ -0,0 +1,154 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Runs part one of the StoredClassCatalogTest. This part is run with the
+ * old/original version of TestSerial in the classpath. It creates a fresh
+ * environment and databases containing serialized versions of the old class.
+ * When StoredClassCatalogTest is run, it will read these objects from the
+ * database created here.
+ *
+ * @author Mark Hayes
+ */
+public class StoredClassCatalogTestInit extends TestCase
+ implements TransactionWorker {
+
+ static final String CATALOG_FILE = StoredClassCatalogTest.CATALOG_FILE;
+ static final String STORE_FILE = StoredClassCatalogTest.STORE_FILE;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ suite.addTest(new StoredClassCatalogTestInit(TestEnv.ALL[i]));
+ }
+ return suite;
+ }
+
+ private TestEnv testEnv;
+ private Environment env;
+ private StoredClassCatalog catalog;
+ private Database store;
+ private Map map;
+ private TransactionRunner runner;
+
+ public StoredClassCatalogTestInit(TestEnv testEnv) {
+
+ super("StoredClassCatalogTestInit-" + testEnv.getName());
+ this.testEnv = testEnv;
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = testEnv.open(StoredClassCatalogTest.makeTestName(testEnv));
+ runner = new TransactionRunner(env);
+
+ catalog = new StoredClassCatalog(openDb(CATALOG_FILE));
+
+ SerialBinding keyBinding = new SerialBinding(catalog, String.class);
+ SerialBinding valueBinding =
+ new SerialBinding(catalog, TestSerial.class);
+ store = openDb(STORE_FILE);
+
+ map = new StoredMap(store, keyBinding, valueBinding, true);
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(testEnv.isTxnMode());
+ config.setAllowCreate(true);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (catalog != null) {
+ catalog.close();
+ catalog.close(); // should have no effect
+ }
+ if (store != null) {
+ store.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.err.println("Ignored exception during tearDown: ");
+ e.printStackTrace();
+ } finally {
+ /* Ensure that GC can cleanup. */
+ catalog = null;
+ store = null;
+ env = null;
+ testEnv = null;
+ map = null;
+ runner = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ runner.run(this);
+ }
+
+ public void doWork() {
+ TestSerial one = new TestSerial(null);
+ TestSerial two = new TestSerial(one);
+ assertNull("Likely the classpath contains the wrong version of the" +
+ " TestSerial class, the 'original' version is required",
+ one.getStringField());
+ assertNull(two.getStringField());
+ map.put("one", one);
+ map.put("two", two);
+ one = (TestSerial) map.get("one");
+ two = (TestSerial) map.get("two");
+ assertEquals(one, two.getOther());
+ assertNull(one.getStringField());
+ assertNull(two.getStringField());
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java b/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java
new file mode 100644
index 00000000..df13cf4b
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java
@@ -0,0 +1,70 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+/**
+ * @see StoredClassCatalogTest
+ * @author Mark Hayes
+ */
+class TestSerial implements java.io.Serializable {
+
+ static final long serialVersionUID = -3738980000390384920L;
+
+ private int i = 123;
+ private TestSerial other;
+
+ // The following field 's' was added after this class was compiled and
+ // serialized instances were saved in resource files. This allows testing
+ // that the original stored instances can be deserialized after changing
+ // the class. The serialVersionUID is needed for this according to Java
+ // serialization rules, and was generated with the serialver tool.
+ //
+ private String s = "string";
+
+ TestSerial(TestSerial other) {
+
+ this.other = other;
+ }
+
+ TestSerial getOther() {
+
+ return other;
+ }
+
+ int getIntField() {
+
+ return i;
+ }
+
+ String getStringField() {
+
+ return s; // this returned null before field 's' was added.
+ }
+
+ public boolean equals(Object object) {
+
+ try {
+ TestSerial o = (TestSerial) object;
+ if ((o.other == null) ? (this.other != null)
+ : (!o.other.equals(this.other))) {
+ return false;
+ }
+ if (this.i != o.i) {
+ return false;
+ }
+ // the following test was not done before field 's' was added
+ if ((o.s == null) ? (this.s != null)
+ : (!o.s.equals(this.s))) {
+ return false;
+ }
+ return true;
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java.original b/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java.original
new file mode 100644
index 00000000..7eef2741
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java.original
@@ -0,0 +1,72 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+/**
+ * @see StoredClassCatalogTest
+ * @author Mark Hayes
+ */
+class TestSerial implements java.io.Serializable
+{
+ static final long serialVersionUID = -3738980000390384920L;
+
+ private int i = 123;
+ private TestSerial other;
+
+ // The following field 's' was added after this class was compiled and
+ // serialized instances were saved in resource files. This allows testing
+ // that the original stored instances can be deserialized after changing
+ // the class. The serialVersionUID is needed for this according to Java
+ // serialization rules, and was generated with the serialver tool.
+ //
+ //private String s = "string";
+
+ TestSerial(TestSerial other)
+ {
+ this.other = other;
+ }
+
+ TestSerial getOther()
+ {
+ return other;
+ }
+
+ int getIntField()
+ {
+ return i;
+ }
+
+ String getStringField()
+ {
+ return null; // this returned null before field 's' was added.
+ }
+
+ public boolean equals(Object object)
+ {
+ try
+ {
+ TestSerial o = (TestSerial) object;
+ if ((o.other == null) ? (this.other != null)
+ : (!o.other.equals(this.other)))
+ return false;
+ if (this.i != o.i)
+ return false;
+ // the following test was not done before field 's' was added
+ /*
+ if ((o.s == null) ? (this.s != null)
+ : (!o.s.equals(this.s)))
+ return false;
+ */
+ return true;
+ }
+ catch (ClassCastException e)
+ {
+ return false;
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/collections/test/serial/TupleSerialFactoryTest.java b/test/scr024/src/com/sleepycat/collections/test/serial/TupleSerialFactoryTest.java
new file mode 100644
index 00000000..d13f8514
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/collections/test/serial/TupleSerialFactoryTest.java
@@ -0,0 +1,245 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.test.MarshalledObject;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.collections.TupleSerialFactory;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class TupleSerialFactoryTest extends TestCase
+ implements TransactionWorker {
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ for (int sorted = 0; sorted < 2; sorted += 1) {
+ suite.addTest(new TupleSerialFactoryTest(TestEnv.ALL[i],
+ sorted != 0));
+ }
+ }
+ return suite;
+ }
+
+ private TestEnv testEnv;
+ private Environment env;
+ private StoredClassCatalog catalog;
+ private TransactionRunner runner;
+ private TupleSerialFactory factory;
+ private Database store1;
+ private Database store2;
+ private SecondaryDatabase index1;
+ private SecondaryDatabase index2;
+ private final boolean isSorted;
+ private Map storeMap1;
+ private Map storeMap2;
+ private Map indexMap1;
+ private Map indexMap2;
+
+ public TupleSerialFactoryTest(TestEnv testEnv, boolean isSorted) {
+
+ super(null);
+
+ this.testEnv = testEnv;
+ this.isSorted = isSorted;
+
+ String name = "TupleSerialFactoryTest-" + testEnv.getName();
+ name += isSorted ? "-sorted" : "-unsorted";
+ setName(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = testEnv.open(getName());
+ runner = new TransactionRunner(env);
+
+ createDatabase();
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (index1 != null) {
+ index1.close();
+ }
+ if (index2 != null) {
+ index2.close();
+ }
+ if (store1 != null) {
+ store1.close();
+ }
+ if (store2 != null) {
+ store2.close();
+ }
+ if (catalog != null) {
+ catalog.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ index1 = null;
+ index2 = null;
+ store1 = null;
+ store2 = null;
+ catalog = null;
+ env = null;
+ testEnv = null;
+ runner = null;
+ factory = null;
+ storeMap1 = null;
+ storeMap2 = null;
+ indexMap1 = null;
+ indexMap2 = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ runner.run(this);
+ }
+
+ public void doWork() {
+ createViews();
+ writeAndRead();
+ }
+
+ private void createDatabase()
+ throws Exception {
+
+ catalog = new StoredClassCatalog(openDb("catalog.db"));
+ factory = new TupleSerialFactory(catalog);
+ assertSame(catalog, factory.getCatalog());
+
+ store1 = openDb("store1.db");
+ store2 = openDb("store2.db");
+ index1 = openSecondaryDb(factory, "1", store1, "index1.db", null);
+ index2 = openSecondaryDb(factory, "2", store2, "index2.db", store1);
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ config.setTransactional(testEnv.isTxnMode());
+ config.setAllowCreate(true);
+ DbCompat.setTypeBtree(config);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ private SecondaryDatabase openSecondaryDb(TupleSerialFactory factory,
+ String keyName,
+ Database primary,
+ String file,
+ Database foreignStore)
+ throws Exception {
+
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(testEnv.isTxnMode());
+ secConfig.setAllowCreate(true);
+ DbCompat.setTypeBtree(secConfig);
+ secConfig.setKeyCreator(factory.getKeyCreator(MarshalledObject.class,
+ keyName));
+ if (foreignStore != null) {
+ secConfig.setForeignKeyDatabase(foreignStore);
+ secConfig.setForeignKeyDeleteAction(
+ ForeignKeyDeleteAction.CASCADE);
+ }
+
+ return DbCompat.testOpenSecondaryDatabase
+ (env, null, file, null, primary, secConfig);
+ }
+
+ private void createViews() {
+ if (isSorted) {
+ storeMap1 = factory.newSortedMap(store1, String.class,
+ MarshalledObject.class, true);
+ storeMap2 = factory.newSortedMap(store2, String.class,
+ MarshalledObject.class, true);
+ indexMap1 = factory.newSortedMap(index1, String.class,
+ MarshalledObject.class, true);
+ indexMap2 = factory.newSortedMap(index2, String.class,
+ MarshalledObject.class, true);
+ } else {
+ storeMap1 = factory.newMap(store1, String.class,
+ MarshalledObject.class, true);
+ storeMap2 = factory.newMap(store2, String.class,
+ MarshalledObject.class, true);
+ indexMap1 = factory.newMap(index1, String.class,
+ MarshalledObject.class, true);
+ indexMap2 = factory.newMap(index2, String.class,
+ MarshalledObject.class, true);
+ }
+ }
+
+ private void writeAndRead() {
+ MarshalledObject o1 = new MarshalledObject("data1", "pk1", "ik1", "");
+ assertNull(storeMap1.put(null, o1));
+
+ assertEquals(o1, storeMap1.get("pk1"));
+ assertEquals(o1, indexMap1.get("ik1"));
+
+ MarshalledObject o2 = new MarshalledObject("data2", "pk2", "", "pk1");
+ assertNull(storeMap2.put(null, o2));
+
+ assertEquals(o2, storeMap2.get("pk2"));
+ assertEquals(o2, indexMap2.get("pk1"));
+
+ /*
+ * store1 contains o1 with primary key "pk1" and index key "ik1"
+ * store2 contains o2 with primary key "pk2" and foreign key "pk1"
+ * which is the primary key of store1
+ */
+
+ storeMap1.remove("pk1");
+ assertNull(storeMap1.get("pk1"));
+ assertNull(indexMap1.get("ik1"));
+ assertNull(storeMap2.get("pk2"));
+ assertNull(indexMap2.get("pk1"));
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/db/util/DualTestCase.java b/test/scr024/src/com/sleepycat/db/util/DualTestCase.java
new file mode 100644
index 00000000..1fbde28a
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/db/util/DualTestCase.java
@@ -0,0 +1,88 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002,2009 Oracle. All rights reserved.
+ *
+ * $Id: DualTestCase.java,v 1.6 2009/01/13 10:41:22 cwl Exp $
+ */
+
+package com.sleepycat.db.util;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+public class DualTestCase extends TestCase {
+
+ private Environment env;
+ private boolean setUpInvoked = false;
+
+ public DualTestCase() {
+ super();
+ }
+
+ protected DualTestCase(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp()
+ throws Exception {
+
+ setUpInvoked = true;
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown()
+ throws Exception {
+
+ if (!setUpInvoked) {
+ throw new IllegalStateException
+ ("tearDown was invoked without a corresponding setUp() call");
+ }
+ destroy();
+ super.tearDown();
+ }
+
+ protected Environment create(File envHome, EnvironmentConfig envConfig)
+ throws DatabaseException {
+
+ try {
+ env = new Environment(envHome, envConfig);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return env;
+ }
+
+ protected void close(Environment environment)
+ throws DatabaseException {
+
+ env.close();
+ env = null;
+ }
+
+ protected void destroy()
+ throws Exception {
+
+ if (env != null) {
+ try {
+ /* Close in case we hit an exception and didn't close */
+ env.close();
+ } catch (RuntimeException e) {
+ /* OK if already closed */
+ }
+ env = null;
+ }
+ }
+
+ public static boolean isReplicatedTest(Class<?> testCaseClass) {
+ return false;
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/BindingTest.java b/test/scr024/src/com/sleepycat/persist/test/BindingTest.java
new file mode 100644
index 00000000..504014ec
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/BindingTest.java
@@ -0,0 +1,2425 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.ForeignMultiKeyNullifier;
+import com.sleepycat.db.SecondaryKeyCreator;
+import com.sleepycat.db.SecondaryMultiKeyCreator;
+import com.sleepycat.persist.impl.PersistCatalog;
+import com.sleepycat.persist.impl.PersistComparator;
+import com.sleepycat.persist.impl.PersistEntityBinding;
+import com.sleepycat.persist.impl.PersistKeyBinding;
+import com.sleepycat.persist.impl.PersistKeyCreator;
+import com.sleepycat.persist.impl.SimpleCatalog;
+import com.sleepycat.persist.model.AnnotationModel;
+import com.sleepycat.persist.model.ClassMetadata;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.EntityMetadata;
+import com.sleepycat.persist.model.EntityModel;
+import com.sleepycat.persist.model.KeyField;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PersistentProxy;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.PrimaryKeyMetadata;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.persist.model.SecondaryKeyMetadata;
+import com.sleepycat.persist.raw.RawField;
+import com.sleepycat.persist.raw.RawObject;
+import com.sleepycat.persist.raw.RawType;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class BindingTest extends TestCase {
+
+ private static final String STORE_PREFIX = "persist#foo#";
+
+ private File envHome;
+ private Environment env;
+ private EntityModel model;
+ private PersistCatalog catalog;
+ private DatabaseEntry keyEntry;
+ private DatabaseEntry dataEntry;
+
+ @Override
+ public void setUp() {
+ envHome = new File(System.getProperty(SharedTestUtils.DEST_DIR));
+ SharedTestUtils.emptyDir(envHome);
+ keyEntry = new DatabaseEntry();
+ dataEntry = new DatabaseEntry();
+ }
+
+ @Override
+ public void tearDown() {
+ if (env != null) {
+ try {
+ env.close();
+ } catch (Exception e) {
+ System.out.println("During tearDown: " + e);
+ }
+ }
+ envHome = null;
+ env = null;
+ catalog = null;
+ keyEntry = null;
+ dataEntry = null;
+ }
+
+ /**
+ * @throws FileNotFoundException from DB core.
+ */
+ private void open()
+ throws FileNotFoundException, DatabaseException {
+
+ EnvironmentConfig envConfig = TestEnv.BDB.getConfig();
+ envConfig.setAllowCreate(true);
+ env = new Environment(envHome, envConfig);
+ openCatalog();
+ }
+
+ private void openCatalog()
+ throws DatabaseException {
+
+ model = new AnnotationModel();
+ model.registerClass(LocalizedTextProxy.class);
+ model.registerClass(LocaleProxy.class);
+
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setAllowCreate(true);
+ DbCompat.setTypeBtree(dbConfig);
+ catalog = new PersistCatalog
+ (null, env, STORE_PREFIX, STORE_PREFIX + "catalog", dbConfig,
+ model, null, false /*rawAccess*/, null /*Store*/);
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ /* Close/open/close catalog to test checks for class evolution. */
+ catalog.close();
+ PersistCatalog.expectNoClassChanges = true;
+ try {
+ openCatalog();
+ } finally {
+ PersistCatalog.expectNoClassChanges = false;
+ }
+ catalog.close();
+ catalog = null;
+
+ env.close();
+ env = null;
+ }
+
+ public void testBasic()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ checkEntity(Basic.class,
+ new Basic(1, "one", 2.2, "three"));
+ checkEntity(Basic.class,
+ new Basic(0, null, 0, null));
+ checkEntity(Basic.class,
+ new Basic(-1, "xxx", -2, "xxx"));
+
+ checkMetadata(Basic.class.getName(), new String[][] {
+ {"id", "long"},
+ {"one", "java.lang.String"},
+ {"two", "double"},
+ {"three", "java.lang.String"},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ close();
+ }
+
+ @Entity
+ static class Basic implements MyEntity {
+
+ @PrimaryKey
+ private long id;
+ private String one;
+ private double two;
+ private String three;
+
+ private Basic() { }
+
+ private Basic(long id, String one, double two, String three) {
+ this.id = id;
+ this.one = one;
+ this.two = two;
+ this.three = three;
+ }
+
+ public String getBasicOne() {
+ return one;
+ }
+
+ public Object getPriKeyObject() {
+ return id;
+ }
+
+ public void validate(Object other) {
+ Basic o = (Basic) other;
+ TestCase.assertEquals(id, o.id);
+ TestCase.assertTrue(nullOrEqual(one, o.one));
+ TestCase.assertEquals(two, o.two);
+ TestCase.assertTrue(nullOrEqual(three, o.three));
+ if (one == three) {
+ TestCase.assertSame(o.one, o.three);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "" + id + ' ' + one + ' ' + two;
+ }
+ }
+
+ public void testSimpleTypes()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ checkEntity(SimpleTypes.class, new SimpleTypes());
+
+ checkMetadata(SimpleTypes.class.getName(), new String[][] {
+ {"f0", "boolean"},
+ {"f1", "char"},
+ {"f2", "byte"},
+ {"f3", "short"},
+ {"f4", "int"},
+ {"f5", "long"},
+ {"f6", "float"},
+ {"f7", "double"},
+ {"f8", "java.lang.String"},
+ {"f9", "java.math.BigInteger"},
+ //{"f10", "java.math.BigDecimal"},
+ {"f11", "java.util.Date"},
+ {"f12", "java.lang.Boolean"},
+ {"f13", "java.lang.Character"},
+ {"f14", "java.lang.Byte"},
+ {"f15", "java.lang.Short"},
+ {"f16", "java.lang.Integer"},
+ {"f17", "java.lang.Long"},
+ {"f18", "java.lang.Float"},
+ {"f19", "java.lang.Double"},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ close();
+ }
+
+ @Entity
+ static class SimpleTypes implements MyEntity {
+
+ @PrimaryKey
+ private final boolean f0 = true;
+ private final char f1 = 'a';
+ private final byte f2 = 123;
+ private final short f3 = 123;
+ private final int f4 = 123;
+ private final long f5 = 123;
+ private final float f6 = 123.4f;
+ private final double f7 = 123.4;
+ private final String f8 = "xxx";
+ private final BigInteger f9 = BigInteger.valueOf(123);
+ //private BigDecimal f10 = BigDecimal.valueOf(123.4);
+ private final Date f11 = new Date();
+ private final Boolean f12 = true;
+ private final Character f13 = 'a';
+ private final Byte f14 = 123;
+ private final Short f15 = 123;
+ private final Integer f16 = 123;
+ private final Long f17 = 123L;
+ private final Float f18 = 123.4f;
+ private final Double f19 = 123.4;
+
+ SimpleTypes() { }
+
+ public Object getPriKeyObject() {
+ return f0;
+ }
+
+ public void validate(Object other) {
+ SimpleTypes o = (SimpleTypes) other;
+ TestCase.assertEquals(f0, o.f0);
+ TestCase.assertEquals(f1, o.f1);
+ TestCase.assertEquals(f2, o.f2);
+ TestCase.assertEquals(f3, o.f3);
+ TestCase.assertEquals(f4, o.f4);
+ TestCase.assertEquals(f5, o.f5);
+ TestCase.assertEquals(f6, o.f6);
+ TestCase.assertEquals(f7, o.f7);
+ TestCase.assertEquals(f8, o.f8);
+ TestCase.assertEquals(f9, o.f9);
+ //TestCase.assertEquals(f10, o.f10);
+ TestCase.assertEquals(f11, o.f11);
+ TestCase.assertEquals(f12, o.f12);
+ TestCase.assertEquals(f13, o.f13);
+ TestCase.assertEquals(f14, o.f14);
+ TestCase.assertEquals(f15, o.f15);
+ TestCase.assertEquals(f16, o.f16);
+ TestCase.assertEquals(f17, o.f17);
+ TestCase.assertEquals(f18, o.f18);
+ TestCase.assertEquals(f19, o.f19);
+ }
+ }
+
+ public void testArrayTypes()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ checkEntity(ArrayTypes.class, new ArrayTypes());
+
+ checkMetadata(ArrayTypes.class.getName(), new String[][] {
+ {"id", "int"},
+ {"f0", boolean[].class.getName()},
+ {"f1", char[].class.getName()},
+ {"f2", byte[].class.getName()},
+ {"f3", short[].class.getName()},
+ {"f4", int[].class.getName()},
+ {"f5", long[].class.getName()},
+ {"f6", float[].class.getName()},
+ {"f7", double[].class.getName()},
+ {"f8", String[].class.getName()},
+ {"f9", Address[].class.getName()},
+ {"f10", boolean[][][].class.getName()},
+ {"f11", String[][][].class.getName()},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ close();
+ }
+
+ @Entity
+ static class ArrayTypes implements MyEntity {
+
+ @PrimaryKey
+ private final int id = 1;
+ private final boolean[] f0 = {false, true};
+ private final char[] f1 = {'a', 'b'};
+ private final byte[] f2 = {1, 2};
+ private final short[] f3 = {1, 2};
+ private final int[] f4 = {1, 2};
+ private final long[] f5 = {1, 2};
+ private final float[] f6 = {1.1f, 2.2f};
+ private final double[] f7 = {1.1, 2,2};
+ private final String[] f8 = {"xxx", null, "yyy"};
+ private final Address[] f9 = {new Address("city", "state", 123),
+ null,
+ new Address("x", "y", 444)};
+ private final boolean[][][] f10 =
+ {
+ {
+ {false, true},
+ {false, true},
+ },
+ null,
+ {
+ {false, true},
+ {false, true},
+ },
+ };
+ private final String[][][] f11 =
+ {
+ {
+ {"xxx", null, "yyy"},
+ null,
+ {"xxx", null, "yyy"},
+ },
+ null,
+ {
+ {"xxx", null, "yyy"},
+ null,
+ {"xxx", null, "yyy"},
+ },
+ };
+
+ ArrayTypes() { }
+
+ public Object getPriKeyObject() {
+ return id;
+ }
+
+ public void validate(Object other) {
+ ArrayTypes o = (ArrayTypes) other;
+ TestCase.assertEquals(id, o.id);
+ TestCase.assertTrue(Arrays.equals(f0, o.f0));
+ TestCase.assertTrue(Arrays.equals(f1, o.f1));
+ TestCase.assertTrue(Arrays.equals(f2, o.f2));
+ TestCase.assertTrue(Arrays.equals(f3, o.f3));
+ TestCase.assertTrue(Arrays.equals(f4, o.f4));
+ TestCase.assertTrue(Arrays.equals(f5, o.f5));
+ TestCase.assertTrue(Arrays.equals(f6, o.f6));
+ TestCase.assertTrue(Arrays.equals(f7, o.f7));
+ TestCase.assertTrue(Arrays.equals(f8, o.f8));
+ TestCase.assertTrue(Arrays.deepEquals(f9, o.f9));
+ TestCase.assertTrue(Arrays.deepEquals(f10, o.f10));
+ TestCase.assertTrue(Arrays.deepEquals(f11, o.f11));
+ }
+ }
+
+ public void testEnumTypes()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ checkEntity(EnumTypes.class, new EnumTypes());
+
+ checkMetadata(EnumTypes.class.getName(), new String[][] {
+ {"f0", "int"},
+ {"f1", Thread.State.class.getName()},
+ {"f2", MyEnum.class.getName()},
+ {"f3", Object.class.getName()},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ close();
+ }
+
+ enum MyEnum { ONE, TWO };
+
+ @Entity
+ static class EnumTypes implements MyEntity {
+
+ @PrimaryKey
+ private final int f0 = 1;
+ private final Thread.State f1 = Thread.State.RUNNABLE;
+ private final MyEnum f2 = MyEnum.ONE;
+ private final Object f3 = MyEnum.TWO;
+
+ EnumTypes() { }
+
+ public Object getPriKeyObject() {
+ return f0;
+ }
+
+ public void validate(Object other) {
+ EnumTypes o = (EnumTypes) other;
+ TestCase.assertEquals(f0, o.f0);
+ TestCase.assertSame(f1, o.f1);
+ TestCase.assertSame(f2, o.f2);
+ TestCase.assertSame(f3, o.f3);
+ }
+ }
+
+ public void testProxyTypes()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ checkEntity(ProxyTypes.class, new ProxyTypes());
+
+ checkMetadata(ProxyTypes.class.getName(), new String[][] {
+ {"f0", "int"},
+ {"f1", Locale.class.getName()},
+ {"f2", Set.class.getName()},
+ {"f3", Set.class.getName()},
+ {"f4", Object.class.getName()},
+ {"f5", HashMap.class.getName()},
+ {"f6", TreeMap.class.getName()},
+ {"f7", List.class.getName()},
+ {"f8", LinkedList.class.getName()},
+ {"f9", LocalizedText.class.getName()},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ close();
+ }
+
+ @Entity
+ static class ProxyTypes implements MyEntity {
+
+ @PrimaryKey
+ private final int f0 = 1;
+ private final Locale f1 = Locale.getDefault();
+ private final Set<Integer> f2 = new HashSet<Integer>();
+ private final Set<Integer> f3 = new TreeSet<Integer>();
+ private final Object f4 = new HashSet<Address>();
+ private final HashMap<String,Integer> f5 =
+ new HashMap<String,Integer>();
+ private final TreeMap<String,Address> f6 =
+ new TreeMap<String,Address>();
+ private final List<Integer> f7 = new ArrayList<Integer>();
+ private final LinkedList<Integer> f8 = new LinkedList<Integer>();
+ private final LocalizedText f9 = new LocalizedText(f1, "xyz");
+
+ ProxyTypes() {
+ f2.add(123);
+ f2.add(456);
+ f3.add(456);
+ f3.add(123);
+ HashSet<Address> s = (HashSet) f4;
+ s.add(new Address("city", "state", 11111));
+ s.add(new Address("city2", "state2", 22222));
+ s.add(new Address("city3", "state3", 33333));
+ f5.put("one", 111);
+ f5.put("two", 222);
+ f5.put("three", 333);
+ f6.put("one", new Address("city", "state", 11111));
+ f6.put("two", new Address("city2", "state2", 22222));
+ f6.put("three", new Address("city3", "state3", 33333));
+ f7.add(123);
+ f7.add(456);
+ f8.add(123);
+ f8.add(456);
+ }
+
+ public Object getPriKeyObject() {
+ return f0;
+ }
+
+ public void validate(Object other) {
+ ProxyTypes o = (ProxyTypes) other;
+ TestCase.assertEquals(f0, o.f0);
+ TestCase.assertEquals(f1, o.f1);
+ TestCase.assertEquals(f2, o.f2);
+ TestCase.assertEquals(f3, o.f3);
+ TestCase.assertEquals(f4, o.f4);
+ TestCase.assertEquals(f5, o.f5);
+ TestCase.assertEquals(f6, o.f6);
+ TestCase.assertEquals(f7, o.f7);
+ TestCase.assertEquals(f8, o.f8);
+ TestCase.assertEquals(f9, o.f9);
+ }
+ }
+
+ @Persistent(proxyFor=Locale.class)
+ static class LocaleProxy implements PersistentProxy<Locale> {
+
+ String language;
+ String country;
+ String variant;
+
+ private LocaleProxy() {}
+
+ public void initializeProxy(Locale object) {
+ language = object.getLanguage();
+ country = object.getCountry();
+ variant = object.getVariant();
+ }
+
+ public Locale convertProxy() {
+ return new Locale(language, country, variant);
+ }
+ }
+
+ static class LocalizedText {
+
+ Locale locale;
+ String text;
+
+ LocalizedText(Locale locale, String text) {
+ this.locale = locale;
+ this.text = text;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ LocalizedText o = (LocalizedText) other;
+ return text.equals(o.text) &&
+ locale.equals(o.locale);
+ }
+ }
+
+ @Persistent(proxyFor=LocalizedText.class)
+ static class LocalizedTextProxy implements PersistentProxy<LocalizedText> {
+
+ Locale locale;
+ String text;
+
+ private LocalizedTextProxy() {}
+
+ public void initializeProxy(LocalizedText object) {
+ locale = object.locale;
+ text = object.text;
+ }
+
+ public LocalizedText convertProxy() {
+ return new LocalizedText(locale, text);
+ }
+ }
+
+ public void testEmbedded()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ Address a1 = new Address("city", "state", 123);
+ Address a2 = new Address("Wikieup", "AZ", 85360);
+
+ checkEntity(Embedded.class,
+ new Embedded("x", a1, a2));
+ checkEntity(Embedded.class,
+ new Embedded("y", a1, null));
+ checkEntity(Embedded.class,
+ new Embedded("", a2, a2));
+
+ checkMetadata(Embedded.class.getName(), new String[][] {
+ {"id", "java.lang.String"},
+ {"idShadow", "java.lang.String"},
+ {"one", Address.class.getName()},
+ {"two", Address.class.getName()},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ checkMetadata(Address.class.getName(), new String[][] {
+ {"street", "java.lang.String"},
+ {"city", "java.lang.String"},
+ {"zip", "int"},
+ },
+ -1 /*priKeyIndex*/, null);
+
+ close();
+ }
+
+ @Entity
+ static class Embedded implements MyEntity {
+
+ @PrimaryKey
+ private String id;
+ private String idShadow;
+ private Address one;
+ private Address two;
+
+ private Embedded() { }
+
+ private Embedded(String id, Address one, Address two) {
+ this.id = id;
+ idShadow = id;
+ this.one = one;
+ this.two = two;
+ }
+
+ public Object getPriKeyObject() {
+ return id;
+ }
+
+ public void validate(Object other) {
+ Embedded o = (Embedded) other;
+ TestCase.assertEquals(id, o.id);
+ if (one != null) {
+ one.validate(o.one);
+ } else {
+ assertNull(o.one);
+ }
+ if (two != null) {
+ two.validate(o.two);
+ } else {
+ assertNull(o.two);
+ }
+ TestCase.assertSame(o.id, o.idShadow);
+ if (one == two) {
+ TestCase.assertSame(o.one, o.two);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "" + id + ' ' + one + ' ' + two;
+ }
+ }
+
+ @Persistent
+ static class Address {
+
+ private String street;
+ private String city;
+ private int zip;
+
+ private Address() {}
+
+ Address(String street, String city, int zip) {
+ this.street = street;
+ this.city = city;
+ this.zip = zip;
+ }
+
+ void validate(Address o) {
+ TestCase.assertTrue(nullOrEqual(street, o.street));
+ TestCase.assertTrue(nullOrEqual(city, o.city));
+ TestCase.assertEquals(zip, o.zip);
+ }
+
+ @Override
+ public String toString() {
+ return "" + street + ' ' + city + ' ' + zip;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+ Address o = (Address) other;
+ return nullOrEqual(street, o.street) &&
+ nullOrEqual(city, o.city) &&
+ nullOrEqual(zip, o.zip);
+ }
+
+ @Override
+ public int hashCode() {
+ return zip;
+ }
+ }
+
+ public void testSubclass()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ checkEntity(Basic.class,
+ new Subclass(-1, "xxx", -2, "xxx", "xxx", true));
+
+ checkMetadata(Basic.class.getName(), new String[][] {
+ {"id", "long"},
+ {"one", "java.lang.String"},
+ {"two", "double"},
+ {"three", "java.lang.String"},
+ },
+ 0 /*priKeyIndex*/, null);
+ checkMetadata(Subclass.class.getName(), new String[][] {
+ {"one", "java.lang.String"},
+ {"two", "boolean"},
+ },
+ -1 /*priKeyIndex*/, Basic.class.getName());
+
+ close();
+ }
+
+ @Persistent
+ static class Subclass extends Basic {
+
+ private String one;
+ private boolean two;
+
+ private Subclass() {
+ }
+
+ private Subclass(long id, String one, double two, String three,
+ String subOne, boolean subTwo) {
+ super(id, one, two, three);
+ this.one = subOne;
+ this.two = subTwo;
+ }
+
+ @Override
+ public void validate(Object other) {
+ super.validate(other);
+ Subclass o = (Subclass) other;
+ TestCase.assertTrue(nullOrEqual(one, o.one));
+ TestCase.assertEquals(two, o.two);
+ if (one == getBasicOne()) {
+ TestCase.assertSame(o.one, o.getBasicOne());
+ }
+ }
+ }
+
+ public void testSuperclass()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ checkEntity(UseSuperclass.class,
+ new UseSuperclass(33, "xxx"));
+
+ checkMetadata(Superclass.class.getName(), new String[][] {
+ {"id", "int"},
+ {"one", "java.lang.String"},
+ },
+ 0 /*priKeyIndex*/, null);
+ checkMetadata(UseSuperclass.class.getName(), new String[][] {
+ },
+ -1 /*priKeyIndex*/, Superclass.class.getName());
+
+ close();
+ }
+
+ @Persistent
+ static class Superclass implements MyEntity {
+
+ @PrimaryKey
+ private int id;
+ private String one;
+
+ private Superclass() { }
+
+ private Superclass(int id, String one) {
+ this.id = id;
+ this.one = one;
+ }
+
+ public Object getPriKeyObject() {
+ return id;
+ }
+
+ public void validate(Object other) {
+ Superclass o = (Superclass) other;
+ TestCase.assertEquals(id, o.id);
+ TestCase.assertTrue(nullOrEqual(one, o.one));
+ }
+ }
+
+ @Entity
+ static class UseSuperclass extends Superclass {
+
+ private UseSuperclass() { }
+
+ private UseSuperclass(int id, String one) {
+ super(id, one);
+ }
+ }
+
+ public void testAbstract()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ checkEntity(EntityUseAbstract.class,
+ new EntityUseAbstract(33, "xxx"));
+
+ checkMetadata(Abstract.class.getName(), new String[][] {
+ {"one", "java.lang.String"},
+ },
+ -1 /*priKeyIndex*/, null);
+ checkMetadata(EmbeddedUseAbstract.class.getName(), new String[][] {
+ {"two", "java.lang.String"},
+ },
+ -1 /*priKeyIndex*/, Abstract.class.getName());
+ checkMetadata(EntityUseAbstract.class.getName(), new String[][] {
+ {"id", "int"},
+ {"f1", EmbeddedUseAbstract.class.getName()},
+ {"f2", Abstract.class.getName()},
+ {"f3", Object.class.getName()},
+ {"f4", Interface.class.getName()},
+ {"a1", EmbeddedUseAbstract[].class.getName()},
+ {"a2", Abstract[].class.getName()},
+ {"a3", Abstract[].class.getName()},
+ {"a4", Object[].class.getName()},
+ {"a5", Interface[].class.getName()},
+ {"a6", Interface[].class.getName()},
+ {"a7", Interface[].class.getName()},
+ },
+ 0 /*priKeyIndex*/, Abstract.class.getName());
+
+ close();
+ }
+
+ @Persistent
+ static abstract class Abstract implements Interface {
+
+ String one;
+
+ private Abstract() { }
+
+ private Abstract(String one) {
+ this.one = one;
+ }
+
+ public void validate(Object other) {
+ Abstract o = (Abstract) other;
+ TestCase.assertTrue(nullOrEqual(one, o.one));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ Abstract o = (Abstract) other;
+ return nullOrEqual(one, o.one);
+ }
+ }
+
+ interface Interface {
+ void validate(Object other);
+ }
+
+ @Persistent
+ static class EmbeddedUseAbstract extends Abstract {
+
+ private String two;
+
+ private EmbeddedUseAbstract() { }
+
+ private EmbeddedUseAbstract(String one, String two) {
+ super(one);
+ this.two = two;
+ }
+
+ @Override
+ public void validate(Object other) {
+ super.validate(other);
+ EmbeddedUseAbstract o = (EmbeddedUseAbstract) other;
+ TestCase.assertTrue(nullOrEqual(two, o.two));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ EmbeddedUseAbstract o = (EmbeddedUseAbstract) other;
+ return nullOrEqual(two, o.two);
+ }
+ }
+
+ @Entity
+ static class EntityUseAbstract extends Abstract implements MyEntity {
+
+ @PrimaryKey
+ private int id;
+
+ private EmbeddedUseAbstract f1;
+ private Abstract f2;
+ private Object f3;
+ private Interface f4;
+ private EmbeddedUseAbstract[] a1;
+ private Abstract[] a2;
+ private Abstract[] a3;
+ private Object[] a4;
+ private Interface[] a5;
+ private Interface[] a6;
+ private Interface[] a7;
+
+ private EntityUseAbstract() { }
+
+ private EntityUseAbstract(int id, String one) {
+ super(one);
+ this.id = id;
+ f1 = new EmbeddedUseAbstract(one, one);
+ f2 = new EmbeddedUseAbstract(one + "x", one + "y");
+ f3 = new EmbeddedUseAbstract(null, null);
+ f4 = new EmbeddedUseAbstract(null, null);
+ a1 = new EmbeddedUseAbstract[3];
+ a2 = new EmbeddedUseAbstract[3];
+ a3 = new Abstract[3];
+ a4 = new Object[3];
+ a5 = new EmbeddedUseAbstract[3];
+ a6 = new Abstract[3];
+ a7 = new Interface[3];
+ for (int i = 0; i < 3; i += 1) {
+ a1[i] = new EmbeddedUseAbstract("1" + i, null);
+ a2[i] = new EmbeddedUseAbstract("2" + i, null);
+ a3[i] = new EmbeddedUseAbstract("3" + i, null);
+ a4[i] = new EmbeddedUseAbstract("4" + i, null);
+ a5[i] = new EmbeddedUseAbstract("5" + i, null);
+ a6[i] = new EmbeddedUseAbstract("6" + i, null);
+ a7[i] = new EmbeddedUseAbstract("7" + i, null);
+ }
+ }
+
+ public Object getPriKeyObject() {
+ return id;
+ }
+
+ @Override
+ public void validate(Object other) {
+ super.validate(other);
+ EntityUseAbstract o = (EntityUseAbstract) other;
+ TestCase.assertEquals(id, o.id);
+ f1.validate(o.f1);
+ assertSame(o.one, o.f1.one);
+ assertSame(o.f1.one, o.f1.two);
+ f2.validate(o.f2);
+ ((Abstract) f3).validate(o.f3);
+ f4.validate(o.f4);
+ assertTrue(arrayToString(a1) + ' ' + arrayToString(o.a1),
+ Arrays.equals(a1, o.a1));
+ assertTrue(Arrays.equals(a2, o.a2));
+ assertTrue(Arrays.equals(a3, o.a3));
+ assertTrue(Arrays.equals(a4, o.a4));
+ assertTrue(Arrays.equals(a5, o.a5));
+ assertTrue(Arrays.equals(a6, o.a6));
+ assertTrue(Arrays.equals(a7, o.a7));
+ assertSame(EmbeddedUseAbstract.class, f2.getClass());
+ assertSame(EmbeddedUseAbstract.class, f3.getClass());
+ assertSame(EmbeddedUseAbstract[].class, a1.getClass());
+ assertSame(EmbeddedUseAbstract[].class, a2.getClass());
+ assertSame(Abstract[].class, a3.getClass());
+ assertSame(Object[].class, a4.getClass());
+ assertSame(EmbeddedUseAbstract[].class, a5.getClass());
+ assertSame(Abstract[].class, a6.getClass());
+ assertSame(Interface[].class, a7.getClass());
+ }
+ }
+
+ public void testCompositeKey()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ CompositeKey key =
+ new CompositeKey(123, 456L, "xyz", BigInteger.valueOf(789),
+ MyEnum.ONE);
+ checkEntity(UseCompositeKey.class,
+ new UseCompositeKey(key, "one"));
+
+ checkMetadata(UseCompositeKey.class.getName(), new String[][] {
+ {"key", CompositeKey.class.getName()},
+ {"one", "java.lang.String"},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ checkMetadata(CompositeKey.class.getName(), new String[][] {
+ {"f1", "int"},
+ {"f2", "java.lang.Long"},
+ {"f3", "java.lang.String"},
+ {"f4", "java.math.BigInteger"},
+ {"f5", MyEnum.class.getName()},
+ },
+ -1 /*priKeyIndex*/, null);
+
+ close();
+ }
+
+ @Persistent
+ static class CompositeKey {
+ @KeyField(3)
+ private int f1;
+ @KeyField(2)
+ private Long f2;
+ @KeyField(1)
+ private String f3;
+ @KeyField(4)
+ private BigInteger f4;
+ @KeyField(5)
+ private MyEnum f5;
+
+ private CompositeKey() {}
+
+ CompositeKey(int f1, Long f2, String f3, BigInteger f4, MyEnum f5) {
+ this.f1 = f1;
+ this.f2 = f2;
+ this.f3 = f3;
+ this.f4 = f4;
+ this.f5 = f5;
+ }
+
+ void validate(CompositeKey o) {
+ TestCase.assertEquals(f1, o.f1);
+ TestCase.assertTrue(nullOrEqual(f2, o.f2));
+ TestCase.assertTrue(nullOrEqual(f3, o.f3));
+ TestCase.assertTrue(nullOrEqual(f4, o.f4));
+ TestCase.assertEquals(f5, o.f5);
+ TestCase.assertTrue(nullOrEqual(f5, o.f5));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ CompositeKey o = (CompositeKey) other;
+ return f1 == o.f1 &&
+ nullOrEqual(f2, o.f2) &&
+ nullOrEqual(f3, o.f3) &&
+ nullOrEqual(f4, o.f4) &&
+ nullOrEqual(f5, o.f5);
+ }
+
+ @Override
+ public int hashCode() {
+ return f1;
+ }
+
+ @Override
+ public String toString() {
+ return "" + f1 + ' ' + f2 + ' ' + f3 + ' ' + f4 + ' ' + f5;
+ }
+ }
+
+ @Entity
+ static class UseCompositeKey implements MyEntity {
+
+ @PrimaryKey
+ private CompositeKey key;
+ private String one;
+
+ private UseCompositeKey() { }
+
+ private UseCompositeKey(CompositeKey key, String one) {
+ this.key = key;
+ this.one = one;
+ }
+
+ public Object getPriKeyObject() {
+ return key;
+ }
+
+ public void validate(Object other) {
+ UseCompositeKey o = (UseCompositeKey) other;
+ TestCase.assertNotNull(key);
+ TestCase.assertNotNull(o.key);
+ key.validate(o.key);
+ TestCase.assertTrue(nullOrEqual(one, o.one));
+ }
+ }
+
+ public void testComparableKey()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ ComparableKey key = new ComparableKey(123, 456);
+ checkEntity(UseComparableKey.class,
+ new UseComparableKey(key, "one"));
+
+ checkMetadata(UseComparableKey.class.getName(), new String[][] {
+ {"key", ComparableKey.class.getName()},
+ {"one", "java.lang.String"},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ checkMetadata(ComparableKey.class.getName(), new String[][] {
+ {"f1", "int"},
+ {"f2", "int"},
+ },
+ -1 /*priKeyIndex*/, null);
+
+ ClassMetadata classMeta =
+ model.getClassMetadata(UseComparableKey.class.getName());
+ assertNotNull(classMeta);
+
+ PersistKeyBinding binding = new PersistKeyBinding
+ (catalog, ComparableKey.class.getName(), false);
+
+ PersistComparator comparator = new PersistComparator
+ (/*ComparableKey.class.getName(),
+ classMeta.getCompositeKeyFields(),*/
+ binding);
+
+ compareKeys(comparator, binding, new ComparableKey(1, 1),
+ new ComparableKey(1, 1), 0);
+ compareKeys(comparator, binding, new ComparableKey(1, 2),
+ new ComparableKey(1, 1), -1);
+ compareKeys(comparator, binding, new ComparableKey(2, 1),
+ new ComparableKey(1, 1), -1);
+ compareKeys(comparator, binding, new ComparableKey(2, 1),
+ new ComparableKey(3, 1), 1);
+
+ close();
+ }
+
+ private void compareKeys(Comparator<byte[]> comparator,
+ EntryBinding binding,
+ Object key1,
+ Object key2,
+ int expectResult) {
+ DatabaseEntry entry1 = new DatabaseEntry();
+ DatabaseEntry entry2 = new DatabaseEntry();
+ binding.objectToEntry(key1, entry1);
+ binding.objectToEntry(key2, entry2);
+ int result = comparator.compare(entry1.getData(), entry2.getData());
+ assertEquals(expectResult, result);
+ }
+
+ @Persistent
+ static class ComparableKey implements Comparable<ComparableKey> {
+ @KeyField(2)
+ private int f1;
+ @KeyField(1)
+ private int f2;
+
+ private ComparableKey() {}
+
+ ComparableKey(int f1, int f2) {
+ this.f1 = f1;
+ this.f2 = f2;
+ }
+
+ void validate(ComparableKey o) {
+ TestCase.assertEquals(f1, o.f1);
+ TestCase.assertEquals(f2, o.f2);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ ComparableKey o = (ComparableKey) other;
+ return f1 == o.f1 && f2 == o.f2;
+ }
+
+ @Override
+ public int hashCode() {
+ return f1 + f2;
+ }
+
+ @Override
+ public String toString() {
+ return "" + f1 + ' ' + f2;
+ }
+
+ /** Compare f1 then f2, in reverse integer order. */
+ public int compareTo(ComparableKey o) {
+ if (f1 != o.f1) {
+ return o.f1 - f1;
+ } else {
+ return o.f2 - f2;
+ }
+ }
+ }
+
+ @Entity
+ static class UseComparableKey implements MyEntity {
+
+ @PrimaryKey
+ private ComparableKey key;
+ private String one;
+
+ private UseComparableKey() { }
+
+ private UseComparableKey(ComparableKey key, String one) {
+ this.key = key;
+ this.one = one;
+ }
+
+ public Object getPriKeyObject() {
+ return key;
+ }
+
+ public void validate(Object other) {
+ UseComparableKey o = (UseComparableKey) other;
+ TestCase.assertNotNull(key);
+ TestCase.assertNotNull(o.key);
+ key.validate(o.key);
+ TestCase.assertTrue(nullOrEqual(one, o.one));
+ }
+ }
+
+ public void testSecKeys()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ SecKeys obj = new SecKeys();
+ checkEntity(SecKeys.class, obj);
+
+ checkMetadata(SecKeys.class.getName(), new String[][] {
+ {"id", "long"},
+ {"f0", "boolean"},
+ {"g0", "boolean"},
+ {"f1", "char"},
+ {"g1", "char"},
+ {"f2", "byte"},
+ {"g2", "byte"},
+ {"f3", "short"},
+ {"g3", "short"},
+ {"f4", "int"},
+ {"g4", "int"},
+ {"f5", "long"},
+ {"g5", "long"},
+ {"f6", "float"},
+ {"g6", "float"},
+ {"f7", "double"},
+ {"g7", "double"},
+ {"f8", "java.lang.String"},
+ {"g8", "java.lang.String"},
+ {"f9", "java.math.BigInteger"},
+ {"g9", "java.math.BigInteger"},
+ //{"f10", "java.math.BigDecimal"},
+ //{"g10", "java.math.BigDecimal"},
+ {"f11", "java.util.Date"},
+ {"g11", "java.util.Date"},
+ {"f12", "java.lang.Boolean"},
+ {"g12", "java.lang.Boolean"},
+ {"f13", "java.lang.Character"},
+ {"g13", "java.lang.Character"},
+ {"f14", "java.lang.Byte"},
+ {"g14", "java.lang.Byte"},
+ {"f15", "java.lang.Short"},
+ {"g15", "java.lang.Short"},
+ {"f16", "java.lang.Integer"},
+ {"g16", "java.lang.Integer"},
+ {"f17", "java.lang.Long"},
+ {"g17", "java.lang.Long"},
+ {"f18", "java.lang.Float"},
+ {"g18", "java.lang.Float"},
+ {"f19", "java.lang.Double"},
+ {"g19", "java.lang.Double"},
+ {"f20", CompositeKey.class.getName()},
+ {"g20", CompositeKey.class.getName()},
+ {"f21", int[].class.getName()},
+ {"g21", int[].class.getName()},
+ {"f22", Integer[].class.getName()},
+ {"g22", Integer[].class.getName()},
+ {"f23", Set.class.getName()},
+ {"g23", Set.class.getName()},
+ {"f24", CompositeKey[].class.getName()},
+ {"g24", CompositeKey[].class.getName()},
+ {"f25", Set.class.getName()},
+ {"g25", Set.class.getName()},
+ {"f26", MyEnum.class.getName()},
+ {"g26", MyEnum.class.getName()},
+ {"f27", MyEnum[].class.getName()},
+ {"g27", MyEnum[].class.getName()},
+ {"f28", Set.class.getName()},
+ {"g28", Set.class.getName()},
+ {"f31", "java.util.Date"},
+ {"f32", "java.lang.Boolean"},
+ {"f33", "java.lang.Character"},
+ {"f34", "java.lang.Byte"},
+ {"f35", "java.lang.Short"},
+ {"f36", "java.lang.Integer"},
+ {"f37", "java.lang.Long"},
+ {"f38", "java.lang.Float"},
+ {"f39", "java.lang.Double"},
+ {"f40", CompositeKey.class.getName()},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ checkSecKey(obj, "f0", obj.f0, Boolean.class);
+ checkSecKey(obj, "f1", obj.f1, Character.class);
+ checkSecKey(obj, "f2", obj.f2, Byte.class);
+ checkSecKey(obj, "f3", obj.f3, Short.class);
+ checkSecKey(obj, "f4", obj.f4, Integer.class);
+ checkSecKey(obj, "f5", obj.f5, Long.class);
+ checkSecKey(obj, "f6", obj.f6, Float.class);
+ checkSecKey(obj, "f7", obj.f7, Double.class);
+ checkSecKey(obj, "f8", obj.f8, String.class);
+ checkSecKey(obj, "f9", obj.f9, BigInteger.class);
+ //checkSecKey(obj, "f10", obj.f10, BigDecimal.class);
+ checkSecKey(obj, "f11", obj.f11, Date.class);
+ checkSecKey(obj, "f12", obj.f12, Boolean.class);
+ checkSecKey(obj, "f13", obj.f13, Character.class);
+ checkSecKey(obj, "f14", obj.f14, Byte.class);
+ checkSecKey(obj, "f15", obj.f15, Short.class);
+ checkSecKey(obj, "f16", obj.f16, Integer.class);
+ checkSecKey(obj, "f17", obj.f17, Long.class);
+ checkSecKey(obj, "f18", obj.f18, Float.class);
+ checkSecKey(obj, "f19", obj.f19, Double.class);
+ checkSecKey(obj, "f20", obj.f20, CompositeKey.class);
+ checkSecKey(obj, "f26", obj.f26, MyEnum.class);
+
+ checkSecMultiKey(obj, "f21", toSet(obj.f21), Integer.class);
+ checkSecMultiKey(obj, "f22", toSet(obj.f22), Integer.class);
+ checkSecMultiKey(obj, "f23", toSet(obj.f23), Integer.class);
+ checkSecMultiKey(obj, "f24", toSet(obj.f24), CompositeKey.class);
+ checkSecMultiKey(obj, "f25", toSet(obj.f25), CompositeKey.class);
+ checkSecMultiKey(obj, "f27", toSet(obj.f27), MyEnum.class);
+ checkSecMultiKey(obj, "f28", toSet(obj.f28), MyEnum.class);
+
+ nullifySecKey(obj, "f8", obj.f8, String.class);
+ nullifySecKey(obj, "f9", obj.f9, BigInteger.class);
+ //nullifySecKey(obj, "f10", obj.f10, BigDecimal.class);
+ nullifySecKey(obj, "f11", obj.f11, Date.class);
+ nullifySecKey(obj, "f12", obj.f12, Boolean.class);
+ nullifySecKey(obj, "f13", obj.f13, Character.class);
+ nullifySecKey(obj, "f14", obj.f14, Byte.class);
+ nullifySecKey(obj, "f15", obj.f15, Short.class);
+ nullifySecKey(obj, "f16", obj.f16, Integer.class);
+ nullifySecKey(obj, "f17", obj.f17, Long.class);
+ nullifySecKey(obj, "f18", obj.f18, Float.class);
+ nullifySecKey(obj, "f19", obj.f19, Double.class);
+ nullifySecKey(obj, "f20", obj.f20, CompositeKey.class);
+ nullifySecKey(obj, "f26", obj.f26, MyEnum.class);
+
+ nullifySecMultiKey(obj, "f21", obj.f21, Integer.class);
+ nullifySecMultiKey(obj, "f22", obj.f22, Integer.class);
+ nullifySecMultiKey(obj, "f23", obj.f23, Integer.class);
+ nullifySecMultiKey(obj, "f24", obj.f24, CompositeKey.class);
+ nullifySecMultiKey(obj, "f25", obj.f25, CompositeKey.class);
+ nullifySecMultiKey(obj, "f27", obj.f27, MyEnum.class);
+ nullifySecMultiKey(obj, "f28", obj.f28, MyEnum.class);
+
+ nullifySecKey(obj, "f31", obj.f31, Date.class);
+ nullifySecKey(obj, "f32", obj.f32, Boolean.class);
+ nullifySecKey(obj, "f33", obj.f33, Character.class);
+ nullifySecKey(obj, "f34", obj.f34, Byte.class);
+ nullifySecKey(obj, "f35", obj.f35, Short.class);
+ nullifySecKey(obj, "f36", obj.f36, Integer.class);
+ nullifySecKey(obj, "f37", obj.f37, Long.class);
+ nullifySecKey(obj, "f38", obj.f38, Float.class);
+ nullifySecKey(obj, "f39", obj.f39, Double.class);
+ nullifySecKey(obj, "f40", obj.f40, CompositeKey.class);
+
+ close();
+ }
+
+ static Set toSet(int[] a) {
+ Set set = new HashSet();
+ for (int i : a) {
+ set.add(i);
+ }
+ return set;
+ }
+
+ static Set toSet(Object[] a) {
+ return new HashSet(Arrays.asList(a));
+ }
+
+ static Set toSet(Set s) {
+ return s;
+ }
+
+ @Entity
+ static class SecKeys implements MyEntity {
+
+ @PrimaryKey
+ long id;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final boolean f0 = false;
+ private final boolean g0 = false;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final char f1 = '1';
+ private final char g1 = '1';
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final byte f2 = 2;
+ private final byte g2 = 2;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final short f3 = 3;
+ private final short g3 = 3;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final int f4 = 4;
+ private final int g4 = 4;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final long f5 = 5;
+ private final long g5 = 5;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final float f6 = 6.6f;
+ private final float g6 = 6.6f;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final double f7 = 7.7;
+ private final double g7 = 7.7;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final String f8 = "8";
+ private final String g8 = "8";
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private BigInteger f9;
+ private BigInteger g9;
+
+ //@SecondaryKey(relate=MANY_TO_ONE)
+ //private BigDecimal f10;
+ //private BigDecimal g10;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Date f11 = new Date(11);
+ private final Date g11 = new Date(11);
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Boolean f12 = true;
+ private final Boolean g12 = true;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Character f13 = '3';
+ private final Character g13 = '3';
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Byte f14 = 14;
+ private final Byte g14 = 14;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Short f15 = 15;
+ private final Short g15 = 15;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Integer f16 = 16;
+ private final Integer g16 = 16;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Long f17= 17L;
+ private final Long g17= 17L;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Float f18 = 18.18f;
+ private final Float g18 = 18.18f;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Double f19 = 19.19;
+ private final Double g19 = 19.19;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final CompositeKey f20 =
+ new CompositeKey(20, 20L, "20", BigInteger.valueOf(20),
+ MyEnum.ONE);
+ private final CompositeKey g20 =
+ new CompositeKey(20, 20L, "20", BigInteger.valueOf(20),
+ MyEnum.TWO);
+
+ private static int[] arrayOfInt = { 100, 101, 102 };
+
+ private static Integer[] arrayOfInteger = { 100, 101, 102 };
+
+ private static CompositeKey[] arrayOfCompositeKey = {
+ new CompositeKey(100, 100L, "100", BigInteger.valueOf(100),
+ MyEnum.ONE),
+ new CompositeKey(101, 101L, "101", BigInteger.valueOf(101),
+ MyEnum.TWO),
+ new CompositeKey(102, 102L, "102", BigInteger.valueOf(102),
+ MyEnum.TWO),
+ };
+
+ private static MyEnum[] arrayOfEnum =
+ new MyEnum[] { MyEnum.ONE, MyEnum.TWO };
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final int[] f21 = arrayOfInt;
+ private final int[] g21 = f21;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final Integer[] f22 = arrayOfInteger;
+ private final Integer[] g22 = f22;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final Set<Integer> f23 = toSet(arrayOfInteger);
+ private final Set<Integer> g23 = f23;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final CompositeKey[] f24 = arrayOfCompositeKey;
+ private final CompositeKey[] g24 = f24;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final Set<CompositeKey> f25 = toSet(arrayOfCompositeKey);
+ private final Set<CompositeKey> g25 = f25;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final MyEnum f26 = MyEnum.TWO;
+ private final MyEnum g26 = f26;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final MyEnum[] f27 = arrayOfEnum;
+ private final MyEnum[] g27 = f27;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final Set<MyEnum> f28 = toSet(arrayOfEnum);
+ private final Set<MyEnum> g28 = f28;
+
+ /* Repeated key values to test shared references. */
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Date f31 = f11;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Boolean f32 = f12;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Character f33 = f13;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Byte f34 = f14;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Short f35 = f15;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Integer f36 = f16;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Long f37= f17;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Float f38 = f18;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final Double f39 = f19;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private final CompositeKey f40 = f20;
+
+ public Object getPriKeyObject() {
+ return id;
+ }
+
+ public void validate(Object other) {
+ SecKeys o = (SecKeys) other;
+ TestCase.assertEquals(id, o.id);
+
+ TestCase.assertEquals(f0, o.f0);
+ TestCase.assertEquals(f1, o.f1);
+ TestCase.assertEquals(f2, o.f2);
+ TestCase.assertEquals(f3, o.f3);
+ TestCase.assertEquals(f4, o.f4);
+ TestCase.assertEquals(f5, o.f5);
+ TestCase.assertEquals(f6, o.f6);
+ TestCase.assertEquals(f7, o.f7);
+ TestCase.assertEquals(f8, o.f8);
+ TestCase.assertEquals(f9, o.f9);
+ //TestCase.assertEquals(f10, o.f10);
+ TestCase.assertEquals(f11, o.f11);
+ TestCase.assertEquals(f12, o.f12);
+ TestCase.assertEquals(f13, o.f13);
+ TestCase.assertEquals(f14, o.f14);
+ TestCase.assertEquals(f15, o.f15);
+ TestCase.assertEquals(f16, o.f16);
+ TestCase.assertEquals(f17, o.f17);
+ TestCase.assertEquals(f18, o.f18);
+ TestCase.assertEquals(f19, o.f19);
+ TestCase.assertEquals(f20, o.f20);
+ TestCase.assertTrue(Arrays.equals(f21, o.f21));
+ TestCase.assertTrue(Arrays.equals(f22, o.f22));
+ TestCase.assertEquals(f23, o.f23);
+ TestCase.assertTrue(Arrays.equals(f24, o.f24));
+ TestCase.assertEquals(f25, o.f25);
+
+ TestCase.assertEquals(g0, o.g0);
+ TestCase.assertEquals(g1, o.g1);
+ TestCase.assertEquals(g2, o.g2);
+ TestCase.assertEquals(g3, o.g3);
+ TestCase.assertEquals(g4, o.g4);
+ TestCase.assertEquals(g5, o.g5);
+ TestCase.assertEquals(g6, o.g6);
+ TestCase.assertEquals(g7, o.g7);
+ TestCase.assertEquals(g8, o.g8);
+ TestCase.assertEquals(g9, o.g9);
+ //TestCase.assertEquals(g10, o.g10);
+ TestCase.assertEquals(g11, o.g11);
+ TestCase.assertEquals(g12, o.g12);
+ TestCase.assertEquals(g13, o.g13);
+ TestCase.assertEquals(g14, o.g14);
+ TestCase.assertEquals(g15, o.g15);
+ TestCase.assertEquals(g16, o.g16);
+ TestCase.assertEquals(g17, o.g17);
+ TestCase.assertEquals(g18, o.g18);
+ TestCase.assertEquals(g19, o.g19);
+ TestCase.assertEquals(g20, o.g20);
+ TestCase.assertTrue(Arrays.equals(g21, o.g21));
+ TestCase.assertTrue(Arrays.equals(g22, o.g22));
+ TestCase.assertEquals(g23, o.g23);
+ TestCase.assertTrue(Arrays.equals(g24, o.g24));
+ TestCase.assertEquals(g25, o.g25);
+
+ TestCase.assertEquals(f31, o.f31);
+ TestCase.assertEquals(f32, o.f32);
+ TestCase.assertEquals(f33, o.f33);
+ TestCase.assertEquals(f34, o.f34);
+ TestCase.assertEquals(f35, o.f35);
+ TestCase.assertEquals(f36, o.f36);
+ TestCase.assertEquals(f37, o.f37);
+ TestCase.assertEquals(f38, o.f38);
+ TestCase.assertEquals(f39, o.f39);
+ TestCase.assertEquals(f40, o.f40);
+
+ checkSameIfNonNull(o.f31, o.f11);
+ checkSameIfNonNull(o.f32, o.f12);
+ checkSameIfNonNull(o.f33, o.f13);
+ checkSameIfNonNull(o.f34, o.f14);
+ checkSameIfNonNull(o.f35, o.f15);
+ checkSameIfNonNull(o.f36, o.f16);
+ checkSameIfNonNull(o.f37, o.f17);
+ checkSameIfNonNull(o.f38, o.f18);
+ checkSameIfNonNull(o.f39, o.f19);
+ checkSameIfNonNull(o.f40, o.f20);
+ }
+ }
+
+ public void testSecKeyRefToPriKey()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ SecKeyRefToPriKey obj = new SecKeyRefToPriKey();
+ checkEntity(SecKeyRefToPriKey.class, obj);
+
+ checkMetadata(SecKeyRefToPriKey.class.getName(), new String[][] {
+ {"priKey", "java.lang.String"},
+ {"secKey1", "java.lang.String"},
+ {"secKey2", String[].class.getName()},
+ {"secKey3", Set.class.getName()},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ checkSecKey(obj, "secKey1", obj.secKey1, String.class);
+ checkSecMultiKey(obj, "secKey2", toSet(obj.secKey2), String.class);
+ checkSecMultiKey(obj, "secKey3", toSet(obj.secKey3), String.class);
+
+ close();
+ }
+
+ @Entity
+ static class SecKeyRefToPriKey implements MyEntity {
+
+ @PrimaryKey
+ private final String priKey;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ private final String secKey1;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final String[] secKey2;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private final Set<String> secKey3 = new HashSet<String>();
+
+ private SecKeyRefToPriKey() {
+ priKey = "sharedValue";
+ secKey1 = priKey;
+ secKey2 = new String[] { priKey };
+ secKey3.add(priKey);
+ }
+
+ public Object getPriKeyObject() {
+ return priKey;
+ }
+
+ public void validate(Object other) {
+ SecKeyRefToPriKey o = (SecKeyRefToPriKey) other;
+ TestCase.assertEquals(priKey, o.priKey);
+ TestCase.assertNotNull(o.secKey1);
+ TestCase.assertEquals(1, o.secKey2.length);
+ TestCase.assertEquals(1, o.secKey3.size());
+ TestCase.assertSame(o.secKey1, o.priKey);
+ TestCase.assertSame(o.secKey2[0], o.priKey);
+ TestCase.assertSame(o.secKey3.iterator().next(), o.priKey);
+ }
+ }
+
+ public void testSecKeyInSuperclass()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ SecKeyInSuperclassEntity obj = new SecKeyInSuperclassEntity();
+ checkEntity(SecKeyInSuperclassEntity.class, obj);
+
+ checkMetadata(SecKeyInSuperclass.class.getName(),
+ new String[][] {
+ {"priKey", "java.lang.String"},
+ {"secKey1", String.class.getName()},
+ },
+ 0/*priKeyIndex*/, null);
+
+ checkMetadata(SecKeyInSuperclassEntity.class.getName(),
+ new String[][] {
+ {"secKey2", "java.lang.String"},
+ },
+ -1 /*priKeyIndex*/, SecKeyInSuperclass.class.getName());
+
+ checkSecKey
+ (obj, SecKeyInSuperclassEntity.class, "secKey1", obj.secKey1,
+ String.class);
+ checkSecKey
+ (obj, SecKeyInSuperclassEntity.class, "secKey2", obj.secKey2,
+ String.class);
+
+ close();
+ }
+
+ @Persistent
+ static class SecKeyInSuperclass implements MyEntity {
+
+ @PrimaryKey
+ String priKey = "1";
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ String secKey1 = "1";
+
+ public Object getPriKeyObject() {
+ return priKey;
+ }
+
+ public void validate(Object other) {
+ SecKeyInSuperclass o = (SecKeyInSuperclass) other;
+ TestCase.assertEquals(secKey1, o.secKey1);
+ }
+ }
+
+ @Entity
+ static class SecKeyInSuperclassEntity extends SecKeyInSuperclass {
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ String secKey2 = "2";
+
+ @Override
+ public void validate(Object other) {
+ super.validate(other);
+ SecKeyInSuperclassEntity o = (SecKeyInSuperclassEntity) other;
+ TestCase.assertEquals(priKey, o.priKey);
+ TestCase.assertEquals(secKey2, o.secKey2);
+ }
+ }
+
+ public void testSecKeyInSubclass()
+ throws FileNotFoundException, DatabaseException {
+
+ open();
+
+ SecKeyInSubclass obj = new SecKeyInSubclass();
+ checkEntity(SecKeyInSubclassEntity.class, obj);
+
+ checkMetadata(SecKeyInSubclassEntity.class.getName(), new String[][] {
+ {"priKey", "java.lang.String"},
+ {"secKey1", "java.lang.String"},
+ },
+ 0 /*priKeyIndex*/, null);
+
+ checkMetadata(SecKeyInSubclass.class.getName(), new String[][] {
+ {"secKey2", String.class.getName()},
+ },
+ -1 /*priKeyIndex*/,
+ SecKeyInSubclassEntity.class.getName());
+
+ checkSecKey
+ (obj, SecKeyInSubclassEntity.class, "secKey1", obj.secKey1,
+ String.class);
+ checkSecKey
+ (obj, SecKeyInSubclassEntity.class, "secKey2", obj.secKey2,
+ String.class);
+
+ close();
+ }
+
+ @Entity
+ static class SecKeyInSubclassEntity implements MyEntity {
+
+ @PrimaryKey
+ String priKey = "1";
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ String secKey1;
+
+ public Object getPriKeyObject() {
+ return priKey;
+ }
+
+ public void validate(Object other) {
+ SecKeyInSubclassEntity o = (SecKeyInSubclassEntity) other;
+ TestCase.assertEquals(priKey, o.priKey);
+ TestCase.assertEquals(secKey1, o.secKey1);
+ }
+ }
+
+ @Persistent
+ static class SecKeyInSubclass extends SecKeyInSubclassEntity {
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ String secKey2 = "2";
+
+ @Override
+ public void validate(Object other) {
+ super.validate(other);
+ SecKeyInSubclass o = (SecKeyInSubclass) other;
+ TestCase.assertEquals(secKey2, o.secKey2);
+ }
+ }
+
+ private static void checkSameIfNonNull(Object o1, Object o2) {
+ if (o1 != null && o2 != null) {
+ assertSame(o1, o2);
+ }
+ }
+
+ private void checkEntity(Class entityCls, MyEntity entity) {
+ Object priKey = entity.getPriKeyObject();
+ Class keyCls = priKey.getClass();
+ DatabaseEntry keyEntry2 = new DatabaseEntry();
+ DatabaseEntry dataEntry2 = new DatabaseEntry();
+
+ /* Write object, read it back and validate (compare) it. */
+ PersistEntityBinding entityBinding =
+ new PersistEntityBinding(catalog, entityCls.getName(), false);
+ entityBinding.objectToData(entity, dataEntry);
+ entityBinding.objectToKey(entity, keyEntry);
+ Object entity2 = entityBinding.entryToObject(keyEntry, dataEntry);
+ entity.validate(entity2);
+
+ /* Read back the primary key and validate it. */
+ PersistKeyBinding keyBinding =
+ new PersistKeyBinding(catalog, keyCls.getName(), false);
+ Object priKey2 = keyBinding.entryToObject(keyEntry);
+ assertEquals(priKey, priKey2);
+ keyBinding.objectToEntry(priKey2, keyEntry2);
+ assertEquals(keyEntry, keyEntry2);
+
+ /* Check raw entity binding. */
+ PersistEntityBinding rawEntityBinding =
+ new PersistEntityBinding(catalog, entityCls.getName(), true);
+ RawObject rawEntity =
+ (RawObject) rawEntityBinding.entryToObject(keyEntry, dataEntry);
+ rawEntityBinding.objectToKey(rawEntity, keyEntry2);
+ rawEntityBinding.objectToData(rawEntity, dataEntry2);
+ entity2 = entityBinding.entryToObject(keyEntry2, dataEntry2);
+ entity.validate(entity2);
+ RawObject rawEntity2 =
+ (RawObject) rawEntityBinding.entryToObject(keyEntry2, dataEntry2);
+ assertEquals(rawEntity, rawEntity2);
+ assertEquals(dataEntry, dataEntry2);
+ assertEquals(keyEntry, keyEntry2);
+
+ /* Check that raw entity can be converted to a regular entity. */
+ entity2 = catalog.convertRawObject(rawEntity, null);
+ entity.validate(entity2);
+
+ /* Check raw key binding. */
+ PersistKeyBinding rawKeyBinding =
+ new PersistKeyBinding(catalog, keyCls.getName(), true);
+ Object rawKey = rawKeyBinding.entryToObject(keyEntry);
+ rawKeyBinding.objectToEntry(rawKey, keyEntry2);
+ priKey2 = keyBinding.entryToObject(keyEntry2);
+ assertEquals(priKey, priKey2);
+ assertEquals(keyEntry, keyEntry2);
+ }
+
+ private void checkSecKey(MyEntity entity,
+ String keyName,
+ Object keyValue,
+ Class keyCls)
+ throws DatabaseException {
+
+ checkSecKey(entity, entity.getClass(), keyName, keyValue, keyCls);
+ }
+
+ private void checkSecKey(MyEntity entity,
+ Class entityCls,
+ String keyName,
+ Object keyValue,
+ Class keyCls)
+ throws DatabaseException {
+
+ /* Get entity metadata. */
+ EntityMetadata entityMeta =
+ model.getEntityMetadata(entityCls.getName());
+ assertNotNull(entityMeta);
+
+ /* Get secondary key metadata. */
+ SecondaryKeyMetadata secKeyMeta =
+ entityMeta.getSecondaryKeys().get(keyName);
+ assertNotNull(secKeyMeta);
+
+ /* Create key creator/nullifier. */
+ SecondaryKeyCreator keyCreator = new PersistKeyCreator
+ (catalog, entityMeta, keyCls.getName(), secKeyMeta,
+ false /*rawAcess*/);
+
+ /* Convert entity to bytes. */
+ PersistEntityBinding entityBinding =
+ new PersistEntityBinding(catalog, entityCls.getName(), false);
+ entityBinding.objectToData(entity, dataEntry);
+ entityBinding.objectToKey(entity, keyEntry);
+
+ /* Extract secondary key bytes from entity bytes. */
+ DatabaseEntry secKeyEntry = new DatabaseEntry();
+ boolean isKeyPresent = keyCreator.createSecondaryKey
+ (null, keyEntry, dataEntry, secKeyEntry);
+ assertEquals(keyValue != null, isKeyPresent);
+
+ /* Convert secondary key bytes back to an object. */
+ PersistKeyBinding keyBinding =
+ new PersistKeyBinding(catalog, keyCls.getName(), false);
+ if (isKeyPresent) {
+ Object keyValue2 = keyBinding.entryToObject(secKeyEntry);
+ assertEquals(keyValue, keyValue2);
+ DatabaseEntry secKeyEntry2 = new DatabaseEntry();
+ keyBinding.objectToEntry(keyValue2, secKeyEntry2);
+ assertEquals(secKeyEntry, secKeyEntry2);
+ }
+ }
+
+ private void checkSecMultiKey(MyEntity entity,
+ String keyName,
+ Set keyValues,
+ Class keyCls)
+ throws DatabaseException {
+
+ /* Get entity metadata. */
+ Class entityCls = entity.getClass();
+ EntityMetadata entityMeta =
+ model.getEntityMetadata(entityCls.getName());
+ assertNotNull(entityMeta);
+
+ /* Get secondary key metadata. */
+ SecondaryKeyMetadata secKeyMeta =
+ entityMeta.getSecondaryKeys().get(keyName);
+ assertNotNull(secKeyMeta);
+
+ /* Create key creator/nullifier. */
+ SecondaryMultiKeyCreator keyCreator = new PersistKeyCreator
+ (catalog, entityMeta, keyCls.getName(), secKeyMeta,
+ false /*rawAcess*/);
+
+ /* Convert entity to bytes. */
+ PersistEntityBinding entityBinding =
+ new PersistEntityBinding(catalog, entityCls.getName(), false);
+ entityBinding.objectToData(entity, dataEntry);
+ entityBinding.objectToKey(entity, keyEntry);
+
+ /* Extract secondary key bytes from entity bytes. */
+ Set<DatabaseEntry> results = new HashSet<DatabaseEntry>();
+ keyCreator.createSecondaryKeys
+ (null, keyEntry, dataEntry, results);
+ assertEquals(keyValues.size(), results.size());
+
+ /* Convert secondary key bytes back to objects. */
+ PersistKeyBinding keyBinding =
+ new PersistKeyBinding(catalog, keyCls.getName(), false);
+ Set keyValues2 = new HashSet();
+ for (DatabaseEntry secKeyEntry : results) {
+ Object keyValue2 = keyBinding.entryToObject(secKeyEntry);
+ keyValues2.add(keyValue2);
+ }
+ assertEquals(keyValues, keyValues2);
+ }
+
+ private void nullifySecKey(MyEntity entity,
+ String keyName,
+ Object keyValue,
+ Class keyCls)
+ throws DatabaseException {
+
+ /* Get entity metadata. */
+ Class entityCls = entity.getClass();
+ EntityMetadata entityMeta =
+ model.getEntityMetadata(entityCls.getName());
+ assertNotNull(entityMeta);
+
+ /* Get secondary key metadata. */
+ SecondaryKeyMetadata secKeyMeta =
+ entityMeta.getSecondaryKeys().get(keyName);
+ assertNotNull(secKeyMeta);
+
+ /* Create key creator/nullifier. */
+ ForeignMultiKeyNullifier keyNullifier = new PersistKeyCreator
+ (catalog, entityMeta, keyCls.getName(), secKeyMeta,
+ false /*rawAcess*/);
+
+ /* Convert entity to bytes. */
+ PersistEntityBinding entityBinding =
+ new PersistEntityBinding(catalog, entityCls.getName(), false);
+ entityBinding.objectToData(entity, dataEntry);
+ entityBinding.objectToKey(entity, keyEntry);
+
+ /* Convert secondary key to bytes. */
+ PersistKeyBinding keyBinding =
+ new PersistKeyBinding(catalog, keyCls.getName(), false);
+ DatabaseEntry secKeyEntry = new DatabaseEntry();
+ if (keyValue != null) {
+ keyBinding.objectToEntry(keyValue, secKeyEntry);
+ }
+
+ /* Nullify secondary key bytes within entity bytes. */
+ boolean isKeyPresent = keyNullifier.nullifyForeignKey
+ (null, keyEntry, dataEntry, secKeyEntry);
+ assertEquals(keyValue != null, isKeyPresent);
+
+ /* Convert modified entity bytes back to an entity. */
+ Object entity2 = entityBinding.entryToObject(keyEntry, dataEntry);
+ setFieldToNull(entity, keyName);
+ entity.validate(entity2);
+
+ /* Do a full check after nullifying it. */
+ checkSecKey(entity, keyName, null, keyCls);
+ }
+
+ private void nullifySecMultiKey(MyEntity entity,
+ String keyName,
+ Object keyValue,
+ Class keyCls)
+ throws DatabaseException {
+
+ /* Get entity metadata. */
+ Class entityCls = entity.getClass();
+ EntityMetadata entityMeta =
+ model.getEntityMetadata(entityCls.getName());
+ assertNotNull(entityMeta);
+
+ /* Get secondary key metadata. */
+ SecondaryKeyMetadata secKeyMeta =
+ entityMeta.getSecondaryKeys().get(keyName);
+ assertNotNull(secKeyMeta);
+
+ /* Create key creator/nullifier. */
+ ForeignMultiKeyNullifier keyNullifier = new PersistKeyCreator
+ (catalog, entityMeta, keyCls.getName(), secKeyMeta,
+ false /*rawAcess*/);
+
+ /* Convert entity to bytes. */
+ PersistEntityBinding entityBinding =
+ new PersistEntityBinding(catalog, entityCls.getName(), false);
+ entityBinding.objectToData(entity, dataEntry);
+ entityBinding.objectToKey(entity, keyEntry);
+
+ /* Get secondary key binding. */
+ PersistKeyBinding keyBinding =
+ new PersistKeyBinding(catalog, keyCls.getName(), false);
+ DatabaseEntry secKeyEntry = new DatabaseEntry();
+
+ /* Nullify one key value at a time until all of them are gone. */
+ while (true) {
+ Object fieldObj = getField(entity, keyName);
+ fieldObj = nullifyFirstElement(fieldObj, keyBinding, secKeyEntry);
+ if (fieldObj == null) {
+ break;
+ }
+ setField(entity, keyName, fieldObj);
+
+ /* Nullify secondary key bytes within entity bytes. */
+ boolean isKeyPresent = keyNullifier.nullifyForeignKey
+ (null, keyEntry, dataEntry, secKeyEntry);
+ assertEquals(keyValue != null, isKeyPresent);
+
+ /* Convert modified entity bytes back to an entity. */
+ Object entity2 = entityBinding.entryToObject(keyEntry, dataEntry);
+ entity.validate(entity2);
+
+ /* Do a full check after nullifying it. */
+ Set keyValues;
+ if (fieldObj instanceof Set) {
+ keyValues = (Set) fieldObj;
+ } else if (fieldObj instanceof Object[]) {
+ keyValues = toSet((Object[]) fieldObj);
+ } else if (fieldObj instanceof int[]) {
+ keyValues = toSet((int[]) fieldObj);
+ } else {
+ throw new IllegalStateException(fieldObj.getClass().getName());
+ }
+ checkSecMultiKey(entity, keyName, keyValues, keyCls);
+ }
+ }
+
+ /**
+ * Nullifies the first element of an array or collection object by removing
+ * it from the array or collection. Returns the resulting array or
+ * collection. Also outputs the removed element to the keyEntry using the
+ * keyBinding.
+ */
+ private Object nullifyFirstElement(Object obj,
+ EntryBinding keyBinding,
+ DatabaseEntry keyEntry) {
+ if (obj instanceof Collection) {
+ Iterator i = ((Collection) obj).iterator();
+ if (i.hasNext()) {
+ Object elem = i.next();
+ i.remove();
+ keyBinding.objectToEntry(elem, keyEntry);
+ return obj;
+ } else {
+ return null;
+ }
+ } else if (obj instanceof Object[]) {
+ Object[] a1 = (Object[]) obj;
+ if (a1.length > 0) {
+ Object[] a2 = (Object[]) Array.newInstance
+ (obj.getClass().getComponentType(), a1.length - 1);
+ System.arraycopy(a1, 1, a2, 0, a2.length);
+ keyBinding.objectToEntry(a1[0], keyEntry);
+ return a2;
+ } else {
+ return null;
+ }
+ } else if (obj instanceof int[]) {
+ int[] a1 = (int[]) obj;
+ if (a1.length > 0) {
+ int[] a2 = new int[a1.length - 1];
+ System.arraycopy(a1, 1, a2, 0, a2.length);
+ keyBinding.objectToEntry(a1[0], keyEntry);
+ return a2;
+ } else {
+ return null;
+ }
+ } else {
+ throw new IllegalStateException(obj.getClass().getName());
+ }
+ }
+
+ private void checkMetadata(String clsName,
+ String[][] nameTypePairs,
+ int priKeyIndex,
+ String superClsName)
+ throws DatabaseException {
+
+ /* Check metadata/types against the live model. */
+ checkMetadata
+ (catalog, model, clsName, nameTypePairs, priKeyIndex,
+ superClsName);
+
+ /*
+ * Open a catalog that uses the stored model.
+ */
+ PersistCatalog storedCatalog = new PersistCatalog
+ (null, env, STORE_PREFIX, STORE_PREFIX + "catalog", null, null,
+ null, false /*useCurrentModel*/, null /*Store*/);
+ EntityModel storedModel = storedCatalog.getResolvedModel();
+
+ /* Check metadata/types against the stored catalog/model. */
+ checkMetadata
+ (storedCatalog, storedModel, clsName, nameTypePairs, priKeyIndex,
+ superClsName);
+
+ storedCatalog.close();
+ }
+
+ private void checkMetadata(PersistCatalog checkCatalog,
+ EntityModel checkModel,
+ String clsName,
+ String[][] nameTypePairs,
+ int priKeyIndex,
+ String superClsName) {
+ ClassMetadata classMeta = checkModel.getClassMetadata(clsName);
+ assertNotNull(clsName, classMeta);
+
+ PrimaryKeyMetadata priKeyMeta = classMeta.getPrimaryKey();
+ if (priKeyIndex >= 0) {
+ assertNotNull(priKeyMeta);
+ String fieldName = nameTypePairs[priKeyIndex][0];
+ String fieldType = nameTypePairs[priKeyIndex][1];
+ assertEquals(priKeyMeta.getName(), fieldName);
+ assertEquals(priKeyMeta.getClassName(), fieldType);
+ assertEquals(priKeyMeta.getDeclaringClassName(), clsName);
+ assertNull(priKeyMeta.getSequenceName());
+ } else {
+ assertNull(priKeyMeta);
+ }
+
+ RawType type = checkCatalog.getFormat(clsName);
+ assertNotNull(type);
+ assertEquals(clsName, type.getClassName());
+ assertEquals(0, type.getVersion());
+ assertTrue(!type.isSimple());
+ assertTrue(!type.isPrimitive());
+ assertTrue(!type.isEnum());
+ assertNull(type.getEnumConstants());
+ assertTrue(!type.isArray());
+ assertEquals(0, type.getDimensions());
+ assertNull(type.getComponentType());
+ RawType superType = type.getSuperType();
+ if (superClsName != null) {
+ assertNotNull(superType);
+ assertEquals(superClsName, superType.getClassName());
+ } else {
+ assertNull(superType);
+ }
+
+ Map<String,RawField> fields = type.getFields();
+ assertNotNull(fields);
+
+ int nFields = nameTypePairs.length;
+ assertEquals(nFields, fields.size());
+
+ for (String[] pair : nameTypePairs) {
+ String fieldName = pair[0];
+ String fieldType = pair[1];
+ Class fieldCls;
+ try {
+ fieldCls = SimpleCatalog.classForName(fieldType);
+ } catch (ClassNotFoundException e) {
+ fail(e.toString());
+ return; /* For compiler */
+ }
+ RawField field = fields.get(fieldName);
+ assertNotNull(field);
+ assertEquals(fieldName, field.getName());
+ type = field.getType();
+ assertNotNull(type);
+ int dim = getArrayDimensions(fieldType);
+ while (dim > 0) {
+ assertEquals(dim, type.getDimensions());
+ assertEquals(dim, getArrayDimensions(fieldType));
+ assertEquals(true, type.isArray());
+ assertEquals(fieldType, type.getClassName());
+ assertEquals(0, type.getVersion());
+ assertTrue(!type.isSimple());
+ assertTrue(!type.isPrimitive());
+ assertTrue(!type.isEnum());
+ assertNull(type.getEnumConstants());
+ fieldType = getArrayComponent(fieldType, dim);
+ type = type.getComponentType();
+ assertNotNull(fieldType, type);
+ dim -= 1;
+ }
+ assertEquals(fieldType, type.getClassName());
+ List<String> enums = getEnumConstants(fieldType);
+ assertEquals(isSimpleType(fieldType), type.isSimple());
+ assertEquals(isPrimitiveType(fieldType), type.isPrimitive());
+ assertNull(type.getComponentType());
+ assertTrue(!type.isArray());
+ assertEquals(0, type.getDimensions());
+ if (enums != null) {
+ assertTrue(type.isEnum());
+ assertEquals(enums, type.getEnumConstants());
+ assertNull(type.getSuperType());
+ } else {
+ assertTrue(!type.isEnum());
+ assertNull(type.getEnumConstants());
+ }
+ }
+ }
+
+ private List<String> getEnumConstants(String clsName) {
+ if (isPrimitiveType(clsName)) {
+ return null;
+ }
+ Class cls;
+ try {
+ cls = Class.forName(clsName);
+ } catch (ClassNotFoundException e) {
+ fail(e.toString());
+ return null; /* Never happens. */
+ }
+ if (!cls.isEnum()) {
+ return null;
+ }
+ List<String> enums = new ArrayList<String>();
+ Object[] vals = cls.getEnumConstants();
+ for (Object val : vals) {
+ enums.add(val.toString());
+ }
+ return enums;
+ }
+
+ private String getArrayComponent(String clsName, int dim) {
+ clsName = clsName.substring(1);
+ if (dim > 1) {
+ return clsName;
+ }
+ if (clsName.charAt(0) == 'L' &&
+ clsName.charAt(clsName.length() - 1) == ';') {
+ return clsName.substring(1, clsName.length() - 1);
+ }
+ if (clsName.length() != 1) {
+ fail();
+ }
+ switch (clsName.charAt(0)) {
+ case 'Z': return "boolean";
+ case 'B': return "byte";
+ case 'C': return "char";
+ case 'D': return "double";
+ case 'F': return "float";
+ case 'I': return "int";
+ case 'J': return "long";
+ case 'S': return "short";
+ default: fail();
+ }
+ return null; /* Should never happen. */
+ }
+
+ private static int getArrayDimensions(String clsName) {
+ int i = 0;
+ while (clsName.charAt(i) == '[') {
+ i += 1;
+ }
+ return i;
+ }
+
+ private static boolean isSimpleType(String clsName) {
+ return isPrimitiveType(clsName) ||
+ clsName.equals("java.lang.Boolean") ||
+ clsName.equals("java.lang.Character") ||
+ clsName.equals("java.lang.Byte") ||
+ clsName.equals("java.lang.Short") ||
+ clsName.equals("java.lang.Integer") ||
+ clsName.equals("java.lang.Long") ||
+ clsName.equals("java.lang.Float") ||
+ clsName.equals("java.lang.Double") ||
+ clsName.equals("java.lang.String") ||
+ clsName.equals("java.math.BigInteger") ||
+ //clsName.equals("java.math.BigDecimal") ||
+ clsName.equals("java.util.Date");
+ }
+
+ private static boolean isPrimitiveType(String clsName) {
+ return clsName.equals("boolean") ||
+ clsName.equals("char") ||
+ clsName.equals("byte") ||
+ clsName.equals("short") ||
+ clsName.equals("int") ||
+ clsName.equals("long") ||
+ clsName.equals("float") ||
+ clsName.equals("double");
+ }
+
+ interface MyEntity {
+ Object getPriKeyObject();
+ void validate(Object other);
+ }
+
+ private static boolean nullOrEqual(Object o1, Object o2) {
+ return (o1 != null) ? o1.equals(o2) : (o2 == null);
+ }
+
+ private static String arrayToString(Object[] array) {
+ StringBuffer buf = new StringBuffer();
+ buf.append('[');
+ for (Object o : array) {
+ if (o instanceof Object[]) {
+ buf.append(arrayToString((Object[]) o));
+ } else {
+ buf.append(o);
+ }
+ buf.append(',');
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+ private void setFieldToNull(Object obj, String fieldName) {
+ try {
+ Field field = obj.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(obj, null);
+ } catch (NoSuchFieldException e) {
+ fail(e.toString());
+ } catch (IllegalAccessException e) {
+ fail(e.toString());
+ }
+ }
+
+ private void setField(Object obj, String fieldName, Object fieldValue) {
+ try {
+ Field field = obj.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(obj, fieldValue);
+ } catch (NoSuchFieldException e) {
+ throw new IllegalStateException(e.toString());
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e.toString());
+ }
+ }
+
+ private Object getField(Object obj, String fieldName) {
+ try {
+ Field field = obj.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field.get(obj);
+ } catch (NoSuchFieldException e) {
+ throw new IllegalStateException(e.toString());
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e.toString());
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/Enhanced0.java b/test/scr024/src/com/sleepycat/persist/test/Enhanced0.java
new file mode 100644
index 00000000..f29a24cf
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/Enhanced0.java
@@ -0,0 +1,36 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+
+/**
+ * For running ASMifier -- before any enhancements.
+ */
+@Entity
+class Enhanced0 {
+
+ @PrimaryKey
+ private String f1;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private int f2;
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private String f3;
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private String f4;
+
+ private int f5;
+ private String f6;
+ private String f7;
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/Enhanced1.java b/test/scr024/src/com/sleepycat/persist/test/Enhanced1.java
new file mode 100644
index 00000000..2eb93bad
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/Enhanced1.java
@@ -0,0 +1,260 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+
+import com.sleepycat.persist.impl.Enhanced;
+import com.sleepycat.persist.impl.EnhancedAccessor;
+import com.sleepycat.persist.impl.EntityInput;
+import com.sleepycat.persist.impl.EntityOutput;
+import com.sleepycat.persist.impl.Format;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+
+/**
+ * For running ASMifier -- adds minimal enhancements.
+ */
+@Entity
+class Enhanced1 implements Enhanced {
+
+ @PrimaryKey
+ private String f1;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private int f2;
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private String f3;
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private String f4;
+
+ private int f5;
+ private String f6;
+ private String f7;
+ private int f8;
+ private int f9;
+ private int f10;
+ private int f11;
+ private int f12;
+
+ static {
+ EnhancedAccessor.registerClass(null, new Enhanced1());
+ }
+
+ public Object bdbNewInstance() {
+ return new Enhanced1();
+ }
+
+ public Object bdbNewArray(int len) {
+ return new Enhanced1[len];
+ }
+
+ public boolean bdbIsPriKeyFieldNullOrZero() {
+ return f1 == null;
+ }
+
+ public void bdbWritePriKeyField(EntityOutput output, Format format) {
+ output.writeKeyObject(f1, format);
+ }
+
+ public void bdbReadPriKeyField(EntityInput input, Format format) {
+ f1 = (String) input.readKeyObject(format);
+ }
+
+ public void bdbWriteSecKeyFields(EntityOutput output) {
+ /* If primary key is an object: */
+ output.registerPriKeyObject(f1);
+ /* Always: */
+ output.writeInt(f2);
+ output.writeObject(f3, null);
+ output.writeObject(f4, null);
+ }
+
+ public void bdbReadSecKeyFields(EntityInput input,
+ int startField,
+ int endField,
+ int superLevel) {
+ /* If primary key is an object: */
+ input.registerPriKeyObject(f1);
+
+ if (superLevel <= 0) {
+ switch (startField) {
+ case 0:
+ f2 = input.readInt();
+ if (endField == 0) break;
+ case 1:
+ f3 = (String) input.readObject();
+ if (endField == 1) break;
+ case 2:
+ f4 = (String) input.readObject();
+ }
+ }
+ }
+
+ public void bdbWriteNonKeyFields(EntityOutput output) {
+ output.writeInt(f5);
+ output.writeObject(f6, null);
+ output.writeObject(f7, null);
+ output.writeInt(f8);
+ output.writeInt(f9);
+ output.writeInt(f10);
+ output.writeInt(f11);
+ output.writeInt(f12);
+ }
+
+ public void bdbReadNonKeyFields(EntityInput input,
+ int startField,
+ int endField,
+ int superLevel) {
+ if (superLevel <= 0) {
+ switch (startField) {
+ case 0:
+ f5 = input.readInt();
+ if (endField == 0) break;
+ case 1:
+ f6 = (String) input.readObject();
+ if (endField == 1) break;
+ case 2:
+ f7 = (String) input.readObject();
+ if (endField == 2) break;
+ case 3:
+ f8 = input.readInt();
+ if (endField == 3) break;
+ case 4:
+ f9 = input.readInt();
+ if (endField == 4) break;
+ case 5:
+ f10 = input.readInt();
+ if (endField == 5) break;
+ case 6:
+ f11 = input.readInt();
+ if (endField == 6) break;
+ case 7:
+ f12 = input.readInt();
+ }
+ }
+ }
+
+ public void bdbWriteCompositeKeyFields(EntityOutput output,
+ Format[] formats) {
+ }
+
+ public void bdbReadCompositeKeyFields(EntityInput input,
+ Format[] formats) {
+ }
+
+ public boolean bdbNullifyKeyField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField,
+ Object keyElement) {
+ if (superLevel > 0) {
+ return false;
+ } else if (isSecField) {
+ switch (field) {
+ case 1:
+ if (f3 != null) {
+ f3 = null;
+ return true;
+ } else {
+ return false;
+ }
+ case 2:
+ if (f4 != null) {
+ f4 = null;
+ return true;
+ } else {
+ return false;
+ }
+ default:
+ return false;
+ }
+ } else {
+ switch (field) {
+ case 1:
+ if (f6 != null) {
+ f6 = null;
+ return true;
+ } else {
+ return false;
+ }
+ case 2:
+ if (f7 != null) {
+ f7 = null;
+ return true;
+ } else {
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
+ }
+
+ public Object bdbGetField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField) {
+ if (superLevel > 0) {
+ } else if (isSecField) {
+ switch (field) {
+ case 0:
+ return Integer.valueOf(f2);
+ case 1:
+ return f3;
+ case 2:
+ return f4;
+ }
+ } else {
+ switch (field) {
+ case 0:
+ return Integer.valueOf(f5);
+ case 1:
+ return f6;
+ case 2:
+ return f7;
+ }
+ }
+ return null;
+ }
+
+ public void bdbSetField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField,
+ Object value) {
+ if (superLevel > 0) {
+ } else if (isSecField) {
+ switch (field) {
+ case 0:
+ f2 = ((Integer) value).intValue();
+ return;
+ case 1:
+ f3 = (String) value;
+ return;
+ case 2:
+ f4 = (String) value;
+ return;
+ }
+ } else {
+ switch (field) {
+ case 0:
+ f5 = ((Integer) value).intValue();
+ return;
+ case 1:
+ f6 = (String) value;
+ return;
+ case 2:
+ f7 = (String) value;
+ return;
+ }
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/Enhanced2.java b/test/scr024/src/com/sleepycat/persist/test/Enhanced2.java
new file mode 100644
index 00000000..5cc2e064
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/Enhanced2.java
@@ -0,0 +1,110 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import com.sleepycat.persist.impl.EnhancedAccessor;
+import com.sleepycat.persist.impl.EntityInput;
+import com.sleepycat.persist.impl.EntityOutput;
+import com.sleepycat.persist.impl.Format;
+import com.sleepycat.persist.model.Persistent;
+
+/**
+ * For running ASMifier -- entity sublcass.
+ */
+@Persistent
+class Enhanced2 extends Enhanced1 {
+
+ static {
+ EnhancedAccessor.registerClass(null, new Enhanced2());
+ }
+
+ public Object bdbNewInstance() {
+ return new Enhanced2();
+ }
+
+ public Object bdbNewArray(int len) {
+ return new Enhanced2[len];
+ }
+
+ public boolean bdbIsPriKeyFieldNullOrZero() {
+ return super.bdbIsPriKeyFieldNullOrZero();
+ }
+
+ public void bdbWritePriKeyField(EntityOutput output, Format format) {
+ super.bdbWritePriKeyField(output, format);
+ }
+
+ public void bdbReadPriKeyField(EntityInput input, Format format) {
+ super.bdbReadPriKeyField(input, format);
+ }
+
+ public void bdbWriteSecKeyFields(EntityOutput output) {
+ super.bdbWriteSecKeyFields(output);
+ }
+
+ public void bdbReadSecKeyFields(EntityInput input,
+ int startField,
+ int endField,
+ int superLevel) {
+ if (superLevel != 0) {
+ super.bdbReadSecKeyFields
+ (input, startField, endField, superLevel - 1);
+ }
+ }
+
+ public void bdbWriteNonKeyFields(EntityOutput output) {
+ super.bdbWriteNonKeyFields(output);
+ }
+
+ public void bdbReadNonKeyFields(EntityInput input,
+ int startField,
+ int endField,
+ int superLevel) {
+ if (superLevel != 0) {
+ super.bdbReadNonKeyFields
+ (input, startField, endField, superLevel - 1);
+ }
+ }
+
+ public boolean bdbNullifyKeyField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField,
+ Object keyElement) {
+ if (superLevel > 0) {
+ return super.bdbNullifyKeyField
+ (o, field, superLevel - 1, isSecField, keyElement);
+ } else {
+ return false;
+ }
+ }
+
+ public Object bdbGetField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField) {
+ if (superLevel > 0) {
+ return super.bdbGetField
+ (o, field, superLevel - 1, isSecField);
+ } else {
+ return null;
+ }
+ }
+
+ public void bdbSetField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField,
+ Object value) {
+ if (superLevel > 0) {
+ super.bdbSetField
+ (o, field, superLevel - 1, isSecField, value);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/Enhanced3.java b/test/scr024/src/com/sleepycat/persist/test/Enhanced3.java
new file mode 100644
index 00000000..5b59a299
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/Enhanced3.java
@@ -0,0 +1,176 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+/*
+import java.math.BigDecimal;
+*/
+import java.math.BigInteger;
+import java.util.Date;
+
+import com.sleepycat.persist.impl.Enhanced;
+import com.sleepycat.persist.impl.EnhancedAccessor;
+import com.sleepycat.persist.impl.EntityInput;
+import com.sleepycat.persist.impl.EntityOutput;
+import com.sleepycat.persist.impl.Format;
+import com.sleepycat.persist.model.KeyField;
+import com.sleepycat.persist.model.Persistent;
+
+/**
+ * For running ASMifier -- a composite key class using all simple data types,
+ * does not follow from previous EnhancedN.java files
+ */
+@Persistent
+class Enhanced3 implements Enhanced {
+
+ enum MyEnum { ONE, TWO };
+
+ @KeyField(1) boolean z;
+ @KeyField(2) char c;
+ @KeyField(3) byte b;
+ @KeyField(4) short s;
+ @KeyField(5) int i;
+ @KeyField(6) long l;
+ @KeyField(7) float f;
+ @KeyField(8) double d;
+
+ @KeyField(9) Boolean zw;
+ @KeyField(10) Character cw;
+ @KeyField(11) Byte bw;
+ @KeyField(12) Short sw;
+ @KeyField(13) Integer iw;
+ @KeyField(14) Long lw;
+ @KeyField(15) Float fw;
+ @KeyField(16) Double dw;
+
+ @KeyField(17) Date date;
+ @KeyField(18) String str;
+ @KeyField(19) MyEnum e;
+ @KeyField(20) BigInteger bigint;
+ /*
+ @KeyField(21) BigDecimal bigdec;
+ */
+
+ static {
+ EnhancedAccessor.registerClass(null, new Enhanced3());
+ }
+
+ public Object bdbNewInstance() {
+ return new Enhanced3();
+ }
+
+ public Object bdbNewArray(int len) {
+ return new Enhanced3[len];
+ }
+
+ public boolean bdbIsPriKeyFieldNullOrZero() {
+ return false;
+ }
+
+ public void bdbWritePriKeyField(EntityOutput output, Format format) {
+ }
+
+ public void bdbReadPriKeyField(EntityInput input, Format format) {
+ }
+
+ public void bdbWriteSecKeyFields(EntityOutput output) {
+ }
+
+ public void bdbReadSecKeyFields(EntityInput input,
+ int startField,
+ int endField,
+ int superLevel) {
+ }
+
+ public void bdbWriteNonKeyFields(EntityOutput output) {
+ }
+
+ public void bdbReadNonKeyFields(EntityInput input,
+ int startField,
+ int endField,
+ int superLevel) {
+ }
+
+ public void bdbWriteCompositeKeyFields(EntityOutput output,
+ Format[] formats) {
+ output.writeBoolean(z);
+ output.writeChar(c);
+ output.writeByte(b);
+ output.writeShort(s);
+ output.writeInt(i);
+ output.writeLong(l);
+ output.writeSortedFloat(f);
+ output.writeSortedDouble(d);
+
+ output.writeBoolean(zw.booleanValue());
+ output.writeChar(cw.charValue());
+ output.writeByte(bw.byteValue());
+ output.writeShort(sw.shortValue());
+ output.writeInt(iw.intValue());
+ output.writeLong(lw.longValue());
+ output.writeSortedFloat(fw.floatValue());
+ output.writeSortedDouble(dw.doubleValue());
+
+ output.writeLong(date.getTime());
+ output.writeString(str);
+ output.writeKeyObject(e, formats[18]);
+ output.writeBigInteger(bigint);
+ }
+
+ public void bdbReadCompositeKeyFields(EntityInput input,
+ Format[] formats) {
+ z = input.readBoolean();
+ c = input.readChar();
+ b = input.readByte();
+ s = input.readShort();
+ i = input.readInt();
+ l = input.readLong();
+ f = input.readSortedFloat();
+ d = input.readSortedDouble();
+
+ zw = Boolean.valueOf(input.readBoolean());
+ cw = Character.valueOf(input.readChar());
+ bw = Byte.valueOf(input.readByte());
+ sw = Short.valueOf(input.readShort());
+ iw = Integer.valueOf(input.readInt());
+ lw = Long.valueOf(input.readLong());
+ fw = Float.valueOf(input.readSortedFloat());
+ dw = Double.valueOf(input.readSortedDouble());
+
+ date = new Date(input.readLong());
+ str = input.readString();
+ e = (MyEnum) input.readKeyObject(formats[18]);
+ bigint = input.readBigInteger();
+ }
+
+ public boolean bdbNullifyKeyField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField,
+ Object keyElement) {
+ // Didn't bother with this one.
+ return false;
+ }
+
+ public Object bdbGetField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField) {
+ // Didn't bother with this one.
+ return null;
+ }
+
+ public void bdbSetField(Object o,
+ int field,
+ int superLevel,
+ boolean isSecField,
+ Object value) {
+ // Didn't bother with this one.
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/EvolveCase.java b/test/scr024/src/com/sleepycat/persist/test/EvolveCase.java
new file mode 100644
index 00000000..4451efa8
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/EvolveCase.java
@@ -0,0 +1,205 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.persist.test;
+
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.evolve.Mutations;
+import com.sleepycat.persist.model.ClassMetadata;
+import com.sleepycat.persist.model.EntityModel;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.raw.RawStore;
+import com.sleepycat.persist.raw.RawType;
+
+@Persistent
+abstract class EvolveCase {
+
+ static final String STORE_NAME = "foo";
+
+ transient boolean updated;
+
+ Mutations getMutations() {
+ return null;
+ }
+
+ void configure(EntityModel model, StoreConfig config) {
+ }
+
+ String getStoreOpenException() {
+ return null;
+ }
+
+ int getNRecordsExpected() {
+ return 1;
+ }
+
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ }
+
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ }
+
+ /**
+ * @throws DatabaseException from subclasses.
+ */
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+ }
+
+ /**
+ * @throws DatabaseException from subclasses.
+ */
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+ }
+
+ /**
+ * @throws DatabaseException from subclasses.
+ */
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+ }
+
+ /**
+ * @throws DatabaseException from subclasses.
+ */
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+ }
+
+ /**
+ * Checks for equality and prints the entire values rather than
+ * abbreviated values like TestCase.assertEquals does.
+ */
+ static void checkEquals(Object expected, Object got) {
+ if ((expected != null) ? (!expected.equals(got)) : (got != null)) {
+ TestCase.fail("Expected:\n" + expected + "\nBut got:\n" + got);
+ }
+ }
+
+ /**
+ * Asserts than an entity database exists or does not exist.
+ */
+ static void assertDbExists(boolean expectExists,
+ Environment env,
+ String entityClassName) {
+ assertDbExists(expectExists, env, entityClassName, null);
+ }
+
+ /**
+ * Checks that an entity class exists or does not exist.
+ */
+ static void checkEntity(boolean exists,
+ EntityModel model,
+ Environment env,
+ String className,
+ int version,
+ String secKeyName) {
+ if (exists) {
+ TestCase.assertNotNull(model.getEntityMetadata(className));
+ ClassMetadata meta = model.getClassMetadata(className);
+ TestCase.assertNotNull(meta);
+ TestCase.assertEquals(version, meta.getVersion());
+ TestCase.assertTrue(meta.isEntityClass());
+
+ RawType raw = model.getRawType(className);
+ TestCase.assertNotNull(raw);
+ TestCase.assertEquals(version, raw.getVersion());
+ } else {
+ TestCase.assertNull(model.getEntityMetadata(className));
+ TestCase.assertNull(model.getClassMetadata(className));
+ TestCase.assertNull(model.getRawType(className));
+ }
+
+ assertDbExists(exists, env, className);
+ if (secKeyName != null) {
+ assertDbExists(exists, env, className, secKeyName);
+ }
+ }
+
+ /**
+ * Checks that a non-entity class exists or does not exist.
+ */
+ static void checkNonEntity(boolean exists,
+ EntityModel model,
+ Environment env,
+ String className,
+ int version) {
+ if (exists) {
+ ClassMetadata meta = model.getClassMetadata(className);
+ TestCase.assertNotNull(meta);
+ TestCase.assertEquals(version, meta.getVersion());
+ TestCase.assertTrue(!meta.isEntityClass());
+
+ RawType raw = model.getRawType(className);
+ TestCase.assertNotNull(raw);
+ TestCase.assertEquals(version, raw.getVersion());
+ } else {
+ TestCase.assertNull(model.getClassMetadata(className));
+ TestCase.assertNull(model.getRawType(className));
+ }
+
+ TestCase.assertNull(model.getEntityMetadata(className));
+ assertDbExists(false, env, className);
+ }
+
+ /**
+ * Asserts than a database expectExists or does not exist. If keyName is
+ * null, checks an entity database. If keyName is non-null, checks a
+ * secondary database.
+ */
+ static void assertDbExists(boolean expectExists,
+ Environment env,
+ String entityClassName,
+ String keyName) {
+ PersistTestUtils.assertDbExists
+ (expectExists, env, STORE_NAME, entityClassName, keyName);
+ }
+
+ static void checkVersions(EntityModel model, String name, int version) {
+ checkVersions(model, new String[] {name}, new int[] {version});
+ }
+
+ static void checkVersions(EntityModel model,
+ String name1,
+ int version1,
+ String name2,
+ int version2) {
+ checkVersions
+ (model, new String[] {name1, name2},
+ new int[] {version1, version2});
+ }
+
+ private static void checkVersions(EntityModel model,
+ String[] names,
+ int[] versions) {
+ List<RawType> all = model.getAllRawTypeVersions(names[0]);
+ TestCase.assertNotNull(all);
+
+ assert names.length == versions.length;
+ TestCase.assertEquals(all.toString(), names.length, all.size());
+
+ Iterator<RawType> iter = all.iterator();
+ for (int i = 0; i < names.length; i += 1) {
+ RawType type = iter.next();
+ TestCase.assertEquals(versions[i], type.getVersion());
+ TestCase.assertEquals(names[i], type.getClassName());
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/EvolveClasses.java b/test/scr024/src/com/sleepycat/persist/test/EvolveClasses.java
new file mode 100644
index 00000000..058aef38
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/EvolveClasses.java
@@ -0,0 +1,6818 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
+
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.evolve.Conversion;
+import com.sleepycat.persist.evolve.Converter;
+import com.sleepycat.persist.evolve.Deleter;
+import com.sleepycat.persist.evolve.EntityConverter;
+import com.sleepycat.persist.evolve.Mutations;
+import com.sleepycat.persist.evolve.Renamer;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.EntityModel;
+import com.sleepycat.persist.model.KeyField;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PersistentProxy;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.persist.raw.RawObject;
+import com.sleepycat.persist.raw.RawStore;
+import com.sleepycat.persist.raw.RawType;
+
+/**
+ * Nested classes are modified versions of classes of the same name in
+ * EvolveClasses.java.original. See EvolveTestBase.java for the steps that are
+ * taken to add a new class (test case).
+ *
+ * @author Mark Hayes
+ */
+class EvolveClasses {
+
+ private static final String PREFIX = EvolveClasses.class.getName() + '$';
+ private static final String CASECLS = EvolveCase.class.getName();
+
+ private static RawObject readRaw(RawStore store,
+ Object key,
+ Object... classVersionPairs)
+ throws DatabaseException {
+
+ return readRaw(store, null, key, classVersionPairs);
+ }
+
+ /**
+ * Reads a raw object and checks its superclass names and versions.
+ */
+ private static RawObject readRaw(RawStore store,
+ String entityClsName,
+ Object key,
+ Object... classVersionPairs)
+ throws DatabaseException {
+
+ TestCase.assertNotNull(store);
+ TestCase.assertNotNull(key);
+
+ if (entityClsName == null) {
+ entityClsName = (String) classVersionPairs[0];
+ }
+ PrimaryIndex<Object,RawObject> index =
+ store.getPrimaryIndex(entityClsName);
+ TestCase.assertNotNull(index);
+
+ RawObject obj = index.get(key);
+ TestCase.assertNotNull(obj);
+
+ checkRawType(obj.getType(), classVersionPairs);
+
+ RawObject superObj = obj.getSuper();
+ for (int i = 2; i < classVersionPairs.length; i += 2) {
+ Object[] a = new Object[classVersionPairs.length - i];
+ System.arraycopy(classVersionPairs, i, a, 0, a.length);
+ TestCase.assertNotNull(superObj);
+ checkRawType(superObj.getType(), a);
+ superObj = superObj.getSuper();
+ }
+
+ return obj;
+ }
+
+ /**
+ * Reads a raw object and checks its superclass names and versions.
+ */
+ private static void checkRawType(RawType type,
+ Object... classVersionPairs) {
+ TestCase.assertNotNull(type);
+ TestCase.assertNotNull(classVersionPairs);
+ TestCase.assertTrue(classVersionPairs.length % 2 == 0);
+
+ for (int i = 0; i < classVersionPairs.length; i += 2) {
+ String clsName = (String) classVersionPairs[i];
+ int clsVersion = (Integer) classVersionPairs[i + 1];
+ TestCase.assertEquals(clsName, type.getClassName());
+ TestCase.assertEquals(clsVersion, type.getVersion());
+ type = type.getSuperType();
+ }
+ TestCase.assertNull(type);
+ }
+
+ /**
+ * Checks that a raw object contains the specified field values. Does not
+ * check superclass fields.
+ */
+ private static void checkRawFields(RawObject obj,
+ Object... nameValuePairs) {
+ TestCase.assertNotNull(obj);
+ TestCase.assertNotNull(obj.getValues());
+ TestCase.assertNotNull(nameValuePairs);
+ TestCase.assertTrue(nameValuePairs.length % 2 == 0);
+
+ Map<String,Object> values = obj.getValues();
+ TestCase.assertEquals(nameValuePairs.length / 2, values.size());
+
+ for (int i = 0; i < nameValuePairs.length; i += 2) {
+ String name = (String) nameValuePairs[i];
+ Object value = nameValuePairs[i + 1];
+ TestCase.assertEquals(name, value, values.get(name));
+ }
+ }
+
+ private static Map<String,Object> makeValues(Object... nameValuePairs) {
+ TestCase.assertTrue(nameValuePairs.length % 2 == 0);
+ Map<String,Object> values = new HashMap<String,Object>();
+ for (int i = 0; i < nameValuePairs.length; i += 2) {
+ values.put((String) nameValuePairs[i], nameValuePairs[i + 1]);
+ }
+ return values;
+ }
+
+ /**
+ * Disallow removing an entity class when no Deleter mutation is specified.
+ */
+ static class DeletedEntity1_ClassRemoved_NoMutation extends EvolveCase {
+
+ private static final String NAME =
+ PREFIX + "DeletedEntity1_ClassRemoved";
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DeletedEntity1_ClassRemoved version: 0 Error: java.lang.ClassNotFoundException: com.sleepycat.persist.test.EvolveClasses$DeletedEntity1_ClassRemoved";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "skey");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "skey", 88);
+ }
+ }
+
+ /**
+ * Allow removing an entity class when a Deleter mutation is specified.
+ */
+ static class DeletedEntity2_ClassRemoved_WithDeleter extends EvolveCase {
+
+ private static final String NAME =
+ PREFIX + "DeletedEntity2_ClassRemoved";
+
+ @Override
+ int getNRecordsExpected() {
+ return 0;
+ }
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(false, model, env, NAME, 0, "skey");
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 0);
+ }
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ return;
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "skey", 88);
+ }
+ }
+
+ /**
+ * Disallow removing the Entity annotation when no Deleter mutation is
+ * specified.
+ */
+ static class DeletedEntity3_AnnotRemoved_NoMutation extends EvolveCase {
+
+ private static final String NAME =
+ DeletedEntity3_AnnotRemoved_NoMutation.class.getName();
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DeletedEntity3_AnnotRemoved_NoMutation version: 0 Error: java.lang.IllegalArgumentException: Class could not be loaded or is not persistent: com.sleepycat.persist.test.EvolveClasses$DeletedEntity3_AnnotRemoved_NoMutation";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "skey");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "skey", 88);
+ }
+ }
+
+ /**
+ * Allow removing the Entity annotation when a Deleter mutation is
+ * specified.
+ */
+ static class DeletedEntity4_AnnotRemoved_WithDeleter extends EvolveCase {
+
+ private static final String NAME =
+ DeletedEntity4_AnnotRemoved_WithDeleter.class.getName();
+
+ @Override
+ int getNRecordsExpected() {
+ return 0;
+ }
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(false, model, env, NAME, 0, "skey");
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 0);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate) {
+ try {
+ store.getPrimaryIndex
+ (Integer.class,
+ DeletedEntity4_AnnotRemoved_WithDeleter.class);
+ TestCase.fail();
+ } catch (Exception e) {
+ checkEquals
+ ("java.lang.IllegalArgumentException: Class could not be loaded or is not an entity class: com.sleepycat.persist.test.EvolveClasses$DeletedEntity4_AnnotRemoved_WithDeleter",
+ e.toString());
+ }
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ return;
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "skey", 88);
+ }
+ }
+
+ /**
+ * Disallow changing the Entity annotation to Persistent when no Deleter
+ * mutation is specified.
+ */
+ @Persistent(version=1)
+ static class DeletedEntity5_EntityToPersist_NoMutation extends EvolveCase {
+
+ private static final String NAME =
+ DeletedEntity5_EntityToPersist_NoMutation.class.getName();
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DeletedEntity5_EntityToPersist_NoMutation version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DeletedEntity5_EntityToPersist_NoMutation version: 1 Error: @Entity switched to/from @Persistent";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "skey");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "skey", 88);
+ }
+ }
+
+ /**
+ * Allow changing the Entity annotation to Persistent when a Deleter
+ * mutation is specified.
+ */
+ @Persistent(version=1)
+ static class DeletedEntity6_EntityToPersist_WithDeleter extends EvolveCase {
+
+ private static final String NAME =
+ DeletedEntity6_EntityToPersist_WithDeleter.class.getName();
+ private static final String NAME2 =
+ Embed_DeletedEntity6_EntityToPersist_WithDeleter.class.getName();
+
+ @Override
+ int getNRecordsExpected() {
+ return 0;
+ }
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkNonEntity(true, model, env, NAME, 1);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ /* Cannot get the primary index for the former entity class. */
+ try {
+ store.getPrimaryIndex
+ (Integer.class,
+ DeletedEntity6_EntityToPersist_WithDeleter.class);
+ TestCase.fail();
+ } catch (Exception e) {
+ checkEquals
+ ("java.lang.IllegalArgumentException: Class could not be loaded or is not an entity class: com.sleepycat.persist.test.EvolveClasses$DeletedEntity6_EntityToPersist_WithDeleter",
+ e.toString());
+ }
+
+ /* Can embed the now persistent class in another entity class. */
+ PrimaryIndex<Long,
+ Embed_DeletedEntity6_EntityToPersist_WithDeleter>
+ index = store.getPrimaryIndex
+ (Long.class,
+ Embed_DeletedEntity6_EntityToPersist_WithDeleter.class);
+
+ if (doUpdate) {
+ Embed_DeletedEntity6_EntityToPersist_WithDeleter embed =
+ new Embed_DeletedEntity6_EntityToPersist_WithDeleter();
+ index.put(embed);
+ embed = index.get(embed.key);
+ /* This new type should exist only after update. */
+ Environment env = store.getEnvironment();
+ EntityModel model = store.getModel();
+ checkEntity(true, model, env, NAME2, 0, null);
+ checkVersions(model, NAME2, 0);
+ }
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ return;
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "skey", 88);
+ }
+ }
+
+ @Entity
+ static class Embed_DeletedEntity6_EntityToPersist_WithDeleter {
+
+ @PrimaryKey
+ long key = 99;
+
+ DeletedEntity6_EntityToPersist_WithDeleter embedded =
+ new DeletedEntity6_EntityToPersist_WithDeleter();
+ }
+
+ /**
+ * Disallow removing a Persistent class when no Deleter mutation is
+ * specified, even when the Entity class that embedded the Persistent class
+ * is deleted properly (by removing the Entity annotation in this case).
+ */
+ static class DeletedPersist1_ClassRemoved_NoMutation extends EvolveCase {
+
+ private static final String NAME =
+ PREFIX + "DeletedPersist1_ClassRemoved";
+
+ private static final String NAME2 =
+ DeletedPersist1_ClassRemoved_NoMutation.class.getName();
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME2, 0));
+ return m;
+ }
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DeletedPersist1_ClassRemoved version: 0 Error: java.lang.ClassNotFoundException: com.sleepycat.persist.test.EvolveClasses$DeletedPersist1_ClassRemoved";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkNonEntity(true, model, env, NAME, 0);
+ checkEntity(true, model, env, NAME2, 0, null);
+ checkVersions(model, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+
+ RawType embedType = store.getModel().getRawType(NAME);
+ checkRawType(embedType, NAME, 0);
+
+ RawObject embed =
+ new RawObject(embedType, makeValues("f", 123), null);
+
+ RawObject obj = readRaw(store, 99, NAME2, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ /**
+ * Allow removing a Persistent class when a Deleter mutation is
+ * specified, and the Entity class that embedded the Persistent class
+ * is also deleted properly (by removing the Entity annotation in this
+ * case).
+ */
+ static class DeletedPersist2_ClassRemoved_WithDeleter extends EvolveCase {
+
+ private static final String NAME =
+ PREFIX + "DeletedPersist2_ClassRemoved";
+ private static final String NAME2 =
+ DeletedPersist2_ClassRemoved_WithDeleter.class.getName();
+
+ @Override
+ int getNRecordsExpected() {
+ return 0;
+ }
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0));
+ m.addDeleter(new Deleter(NAME2, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkNonEntity(false, model, env, NAME, 0);
+ checkEntity(false, model, env, NAME2, 0, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate) {
+ try {
+ store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist2_ClassRemoved_WithDeleter.class);
+ TestCase.fail();
+ } catch (Exception e) {
+ checkEquals
+ ("java.lang.IllegalArgumentException: Class could not be loaded or is not an entity class: com.sleepycat.persist.test.EvolveClasses$DeletedPersist2_ClassRemoved_WithDeleter",
+ e.toString());
+ }
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ return;
+ }
+
+ RawType embedType = store.getModel().getRawType(NAME);
+ checkRawType(embedType, NAME, 0);
+
+ RawObject embed =
+ new RawObject(embedType, makeValues("f", 123), null);
+
+ RawObject obj = readRaw(store, 99, NAME2, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ static class DeletedPersist3_AnnotRemoved {
+
+ int f = 123;
+ }
+
+ /**
+ * Disallow removing the Persistent annotation when no Deleter mutation is
+ * specified, even when the Entity class that embedded the Persistent class
+ * is deleted properly (by removing the Entity annotation in this case).
+ */
+ static class DeletedPersist3_AnnotRemoved_NoMutation extends EvolveCase {
+
+ private static final String NAME =
+ DeletedPersist3_AnnotRemoved.class.getName();
+ private static final String NAME2 =
+ DeletedPersist3_AnnotRemoved_NoMutation.class.getName();
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME2, 0));
+ return m;
+ }
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DeletedPersist3_AnnotRemoved version: 0 Error: java.lang.IllegalArgumentException: Class could not be loaded or is not persistent: com.sleepycat.persist.test.EvolveClasses$DeletedPersist3_AnnotRemoved";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkNonEntity(true, model, env, NAME, 0);
+ checkEntity(true, model, env, NAME2, 0, null);
+ checkVersions(model, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+
+ RawType embedType = store.getModel().getRawType(NAME);
+ checkRawType(embedType, NAME, 0);
+
+ RawObject embed =
+ new RawObject(embedType, makeValues("f", 123), null);
+
+ RawObject obj = readRaw(store, 99, NAME2, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ static class DeletedPersist4_AnnotRemoved {
+
+ int f = 123;
+ }
+
+ /**
+ * Allow removing the Persistent annotation when a Deleter mutation is
+ * specified, and the Entity class that embedded the Persistent class
+ * is also be deleted properly (by removing the Entity annotation in this
+ * case).
+ */
+ static class DeletedPersist4_AnnotRemoved_WithDeleter extends EvolveCase {
+
+ private static final String NAME =
+ DeletedPersist4_AnnotRemoved.class.getName();
+ private static final String NAME2 =
+ DeletedPersist4_AnnotRemoved_WithDeleter.class.getName();
+
+ @Override
+ int getNRecordsExpected() {
+ return 0;
+ }
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0));
+ m.addDeleter(new Deleter(NAME2, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkNonEntity(false, model, env, NAME, 0);
+ checkEntity(false, model, env, NAME2, 0, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate) {
+ try {
+ store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist4_AnnotRemoved_WithDeleter.class);
+ TestCase.fail();
+ } catch (Exception e) {
+ checkEquals
+ ("java.lang.IllegalArgumentException: Class could not be loaded or is not an entity class: com.sleepycat.persist.test.EvolveClasses$DeletedPersist4_AnnotRemoved_WithDeleter",
+ e.toString());
+ }
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ return;
+ }
+
+ RawType embedType = store.getModel().getRawType(NAME);
+ checkRawType(embedType, NAME, 0);
+
+ RawObject embed =
+ new RawObject(embedType, makeValues("f", 123), null);
+
+ RawObject obj = readRaw(store, 99, NAME2, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ @Entity(version=1)
+ static class DeletedPersist5_PersistToEntity {
+
+ @PrimaryKey
+ int key = 99;
+
+ int f = 123;
+ }
+
+ /**
+ * Disallow changing the Entity annotation to Persistent when no Deleter
+ * mutation is specified, even when the Entity class that embedded the
+ * Persistent class is deleted properly (by removing the Entity annotation
+ * in this case).
+ */
+ static class DeletedPersist5_PersistToEntity_NoMutation
+ extends EvolveCase {
+
+ private static final String NAME =
+ DeletedPersist5_PersistToEntity.class.getName();
+ private static final String NAME2 =
+ DeletedPersist5_PersistToEntity_NoMutation.class.getName();
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME2, 0));
+ return m;
+ }
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DeletedPersist5_PersistToEntity version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DeletedPersist5_PersistToEntity version: 1 Error: @Entity switched to/from @Persistent";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkNonEntity(true, model, env, NAME, 0);
+ checkEntity(true, model, env, NAME2, 0, null);
+ checkVersions(model, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+
+ RawType embedType = store.getModel().getRawType(NAME);
+ checkRawType(embedType, NAME, 0);
+
+ RawObject embed =
+ new RawObject(embedType, makeValues("f", 123), null);
+
+ RawObject obj = readRaw(store, 99, NAME2, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ @Entity(version=1)
+ static class DeletedPersist6_PersistToEntity {
+
+ @PrimaryKey
+ int key = 99;
+
+ int f = 123;
+ }
+
+ /**
+ * Allow changing the Entity annotation to Persistent when a Deleter
+ * mutation is specified, and the Entity class that embedded the Persistent
+ * class is also be deleted properly (by removing the Entity annotation in
+ * this case).
+ */
+ static class DeletedPersist6_PersistToEntity_WithDeleter
+ extends EvolveCase {
+
+ private static final String NAME =
+ DeletedPersist6_PersistToEntity.class.getName();
+ private static final String NAME2 =
+ DeletedPersist6_PersistToEntity_WithDeleter.class.getName();
+
+ @Override
+ int getNRecordsExpected() {
+ return 0;
+ }
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0));
+ m.addDeleter(new Deleter(NAME2, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(false, model, env, NAME2, 0, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ /* Cannot get the primary index for the former entity class. */
+ try {
+ store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist6_PersistToEntity_WithDeleter.class);
+ TestCase.fail();
+ } catch (Exception e) {
+ checkEquals
+ ("java.lang.IllegalArgumentException: Class could not be loaded or is not an entity class: com.sleepycat.persist.test.EvolveClasses$DeletedPersist6_PersistToEntity_WithDeleter",
+ e.toString());
+ }
+
+ /* Can use the primary index of the now entity class. */
+ PrimaryIndex<Integer,
+ DeletedPersist6_PersistToEntity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist6_PersistToEntity.class);
+
+ if (doUpdate) {
+ DeletedPersist6_PersistToEntity obj =
+ new DeletedPersist6_PersistToEntity();
+ index.put(obj);
+ obj = index.get(obj.key);
+ /* This new type should exist only after update. */
+ Environment env = store.getEnvironment();
+ EntityModel model = store.getModel();
+ checkEntity(true, model, env, NAME, 1, null);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,
+ DeletedPersist6_PersistToEntity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist6_PersistToEntity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((DeletedPersist6_PersistToEntity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ return;
+ }
+
+ RawType embedType = store.getModel().getRawType(NAME);
+ checkRawType(embedType, NAME, 0);
+
+ RawObject embed =
+ new RawObject(embedType, makeValues("f", 123), null);
+
+ RawObject obj = readRaw(store, 99, NAME2, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ /**
+ * Disallow renaming an entity class without a Renamer mutation.
+ */
+ @Entity(version=1)
+ static class RenamedEntity1_NewEntityName_NoMutation
+ extends EvolveCase {
+
+ private static final String NAME =
+ PREFIX + "RenamedEntity1_NewEntityName";
+ private static final String NAME2 =
+ RenamedEntity1_NewEntityName_NoMutation.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$RenamedEntity1_NewEntityName version: 0 Error: java.lang.ClassNotFoundException: com.sleepycat.persist.test.EvolveClasses$RenamedEntity1_NewEntityName";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "skey");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "skey", 88);
+ }
+ }
+
+ /**
+ * Allow renaming an entity class with a Renamer mutation.
+ */
+ @Entity(version=1)
+ static class RenamedEntity2_NewEntityName_WithRenamer
+ extends EvolveCase {
+
+ private static final String NAME =
+ PREFIX + "RenamedEntity2_NewEntityName";
+ private static final String NAME2 =
+ RenamedEntity2_NewEntityName_WithRenamer.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addRenamer(new Renamer(NAME, 0, NAME2));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(false, model, env, NAME, 0, null);
+ checkEntity(true, model, env, NAME2, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,RenamedEntity2_NewEntityName_WithRenamer>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ RenamedEntity2_NewEntityName_WithRenamer.class);
+ RenamedEntity2_NewEntityName_WithRenamer obj = index.get(key);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.skey);
+
+ SecondaryIndex<Integer,Integer,
+ RenamedEntity2_NewEntityName_WithRenamer>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ obj = sindex.get(88);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.skey);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,RenamedEntity2_NewEntityName_WithRenamer>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ RenamedEntity2_NewEntityName_WithRenamer.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME2).get(99);
+ index.put((RenamedEntity2_NewEntityName_WithRenamer)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj;
+ if (expectEvolved) {
+ obj = readRaw(store, 99, NAME2, 1, CASECLS, 0);
+ } else {
+ obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ }
+ checkRawFields(obj, "key", 99, "skey", 88);
+ }
+ }
+
+ @Persistent
+ static class DeleteSuperclass1_BaseClass
+ extends EvolveCase {
+
+ int f = 123;
+ }
+
+ /**
+ * Disallow deleting a superclass from the hierarchy when the superclass
+ * has persistent fields and no Deleter or Converter is specified.
+ */
+ @Entity
+ static class DeleteSuperclass1_NoMutation
+ extends EvolveCase {
+
+ private static final String NAME =
+ DeleteSuperclass1_BaseClass.class.getName();
+ private static final String NAME2 =
+ DeleteSuperclass1_NoMutation.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DeleteSuperclass1_NoMutation version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DeleteSuperclass1_NoMutation version: 0 Error: When a superclass is removed from the class hierarchy, the superclass or all of its persistent fields must be deleted with a Deleter: com.sleepycat.persist.test.EvolveClasses$DeleteSuperclass1_BaseClass";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkNonEntity(true, model, env, NAME, 0);
+ checkEntity(true, model, env, NAME2, 0, null);
+ checkVersions(model, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME2, 0, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ checkRawFields(obj.getSuper(), "f", 123);
+ checkRawFields(obj.getSuper().getSuper());
+ }
+ }
+
+ @Persistent
+ static class DeleteSuperclass2_BaseClass
+ extends EvolveCase {
+
+ int f;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey;
+ }
+
+ /**
+ * Allow deleting a superclass from the hierarchy when the superclass has
+ * persistent fields and a class Converter is specified. Also check that
+ * the secondary key field in the deleted base class is handled properly.
+ */
+ @Entity(version=1)
+ static class DeleteSuperclass2_WithConverter extends EvolveCase {
+
+ private static final String NAME =
+ DeleteSuperclass2_BaseClass.class.getName();
+ private static final String NAME2 =
+ DeleteSuperclass2_WithConverter.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ int ff;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Integer skey2;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey3;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addConverter(new EntityConverter
+ (NAME2, 0, new MyConversion(),
+ Collections.singleton("skey")));
+ return m;
+ }
+
+ @SuppressWarnings("serial")
+ static class MyConversion implements Conversion {
+
+ transient RawType newType;
+
+ public void initialize(EntityModel model) {
+ newType = model.getRawType(NAME2);
+ TestCase.assertNotNull(newType);
+ }
+
+ public Object convert(Object fromValue) {
+ TestCase.assertNotNull(newType);
+ RawObject obj = (RawObject) fromValue;
+ RawObject newSuper = obj.getSuper().getSuper();
+ return new RawObject(newType, obj.getValues(), newSuper);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof MyConversion;
+ }
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME2, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ checkNonEntity(true, model, env, NAME, 0);
+ checkVersions(model, NAME, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass2_WithConverter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass2_WithConverter.class);
+ DeleteSuperclass2_WithConverter obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertSame
+ (EvolveCase.class, obj.getClass().getSuperclass());
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.ff);
+ TestCase.assertEquals(Integer.valueOf(77), obj.skey2);
+ TestCase.assertEquals(66, obj.skey3);
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass2_WithConverter>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass2_WithConverter.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME2).get(99);
+ index.put((DeleteSuperclass2_WithConverter)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj;
+ if (expectEvolved) {
+ obj = readRaw(store, 99, NAME2, 1, CASECLS, 0);
+ } else {
+ obj = readRaw(store, 99, NAME2, 0, NAME, 0, CASECLS, 0);
+ }
+ checkRawFields
+ (obj, "key", 99, "ff", 88, "skey2", 77, "skey3", 66);
+ if (expectEvolved) {
+ checkRawFields(obj.getSuper());
+ } else {
+ checkRawFields(obj.getSuper(), "f", 123, "skey", 456);
+ checkRawFields(obj.getSuper().getSuper());
+ }
+ Environment env = store.getEnvironment();
+ assertDbExists(!expectEvolved, env, NAME2, "skey");
+ assertDbExists(true, env, NAME2, "skey3");
+ }
+ }
+
+ static class DeleteSuperclass3_BaseClass
+ extends EvolveCase {
+
+ int f;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey;
+ }
+
+ /**
+ * Allow deleting a superclass from the hierarchy when the superclass
+ * has persistent fields and a class Deleter is specified. Also check that
+ * the secondary key field in the deleted base class is handled properly.
+ */
+ @Entity(version=1)
+ static class DeleteSuperclass3_WithDeleter extends EvolveCase {
+
+ private static final String NAME =
+ DeleteSuperclass3_BaseClass.class.getName();
+ private static final String NAME2 =
+ DeleteSuperclass3_WithDeleter.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ int ff;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME2, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ checkNonEntity(false, model, env, NAME, 0);
+ checkVersions(model, NAME, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass3_WithDeleter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass3_WithDeleter.class);
+ DeleteSuperclass3_WithDeleter obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertSame
+ (EvolveCase.class, obj.getClass().getSuperclass());
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.ff);
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass3_WithDeleter>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass3_WithDeleter.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME2).get(99);
+ index.put((DeleteSuperclass3_WithDeleter)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj;
+ if (expectEvolved) {
+ obj = readRaw(store, 99, NAME2, 1, CASECLS, 0);
+ } else {
+ obj = readRaw(store, 99, NAME2, 0, NAME, 0, CASECLS, 0);
+ }
+ checkRawFields(obj, "key", 99, "ff", 88);
+ if (expectEvolved) {
+ checkRawFields(obj.getSuper());
+ } else {
+ checkRawFields(obj.getSuper(), "f", 123, "skey", 456);
+ checkRawFields(obj.getSuper().getSuper());
+ }
+ Environment env = store.getEnvironment();
+ assertDbExists(!expectEvolved, env, NAME2, "skey");
+ }
+ }
+
+ @Persistent
+ static class DeleteSuperclass4_BaseClass
+ extends EvolveCase {
+ }
+
+ /**
+ * Allow deleting a superclass from the hierarchy when the superclass
+ * has NO persistent fields. No mutations are needed.
+ */
+ @Entity(version=1)
+ static class DeleteSuperclass4_NoFields extends EvolveCase {
+
+ private static final String NAME =
+ DeleteSuperclass4_BaseClass.class.getName();
+ private static final String NAME2 =
+ DeleteSuperclass4_NoFields.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff;
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME2, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ checkNonEntity(true, model, env, NAME, 0);
+ checkVersions(model, NAME, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass4_NoFields>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass4_NoFields.class);
+ DeleteSuperclass4_NoFields obj = index.get(key);
+ TestCase.assertNotNull(obj);
+ TestCase.assertSame
+ (EvolveCase.class, obj.getClass().getSuperclass());
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.ff);
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass4_NoFields>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass4_NoFields.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME2).get(99);
+ index.put((DeleteSuperclass4_NoFields)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj;
+ if (expectEvolved) {
+ obj = readRaw(store, 99, NAME2, 1, CASECLS, 0);
+ } else {
+ obj = readRaw(store, 99, NAME2, 0, NAME, 0, CASECLS, 0);
+ }
+ checkRawFields(obj, "key", 99, "ff", 88);
+ checkRawFields(obj.getSuper());
+ if (expectEvolved) {
+ TestCase.assertNull(obj.getSuper().getSuper());
+ } else {
+ checkRawFields(obj.getSuper().getSuper());
+ }
+ }
+ }
+
+ @Persistent(version=1)
+ static class DeleteSuperclass5_Embedded {
+
+ int f;
+
+ @Override
+ public String toString() {
+ return "" + f;
+ }
+ }
+
+ /**
+ * Ensure that a superclass at the top of the hierarchy can be deleted. A
+ * class Deleter is used.
+ */
+ @Entity
+ static class DeleteSuperclass5_Top
+ extends EvolveCase {
+
+ private static final String NAME =
+ DeleteSuperclass5_Top.class.getName();
+ private static final String NAME2 =
+ DeleteSuperclass5_Embedded.class.getName();
+ private static final String NAME3 =
+ PREFIX + "DeleteSuperclass5_Embedded_Base";
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff;
+
+ DeleteSuperclass5_Embedded embed =
+ new DeleteSuperclass5_Embedded();
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME3, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkNonEntity(true, model, env, NAME2, 1);
+ checkNonEntity(false, model, env, NAME3, 0);
+ checkVersions(model, NAME, 0);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ checkVersions(model, NAME3, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass5_Top>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass5_Top.class);
+ DeleteSuperclass5_Top obj = index.get(key);
+ TestCase.assertNotNull(obj);
+ TestCase.assertNotNull(obj.embed);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.ff);
+ TestCase.assertEquals(123, obj.embed.f);
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass5_Top>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass5_Top.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((DeleteSuperclass5_Top)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawType embedType = store.getModel().getRawType(NAME2);
+ RawObject embedSuper = null;
+ if (!expectEvolved) {
+ RawType embedSuperType = store.getModel().getRawType(NAME3);
+ embedSuper = new RawObject
+ (embedSuperType, makeValues("g", 456), null);
+ }
+ RawObject embed =
+ new RawObject(embedType, makeValues("f", 123), embedSuper);
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88, "embed", embed);
+ }
+ }
+
+ @Persistent
+ static class InsertSuperclass1_BaseClass
+ extends EvolveCase {
+
+ int f = 123;
+ }
+
+ /**
+ * Allow inserting a superclass between two existing classes in the
+ * hierarchy. No mutations are needed.
+ */
+ @Entity(version=1)
+ static class InsertSuperclass1_Between
+ extends InsertSuperclass1_BaseClass {
+
+ private static final String NAME =
+ InsertSuperclass1_BaseClass.class.getName();
+ private static final String NAME2 =
+ InsertSuperclass1_Between.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff;
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkNonEntity(true, model, env, NAME, 0);
+ checkEntity(true, model, env, NAME2, 1, null);
+ checkVersions(model, NAME, 0);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertSuperclass1_Between>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ InsertSuperclass1_Between.class);
+ InsertSuperclass1_Between obj = index.get(key);
+ TestCase.assertNotNull(obj);
+ TestCase.assertSame
+ (InsertSuperclass1_BaseClass.class,
+ obj.getClass().getSuperclass());
+ TestCase.assertSame
+ (EvolveCase.class,
+ obj.getClass().getSuperclass().getSuperclass());
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.ff);
+ TestCase.assertEquals(123, obj.f);
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertSuperclass1_Between>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ InsertSuperclass1_Between.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME2).get(99);
+ index.put((InsertSuperclass1_Between)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj;
+ if (expectEvolved) {
+ obj = readRaw(store, 99, NAME2, 1, NAME, 0, CASECLS, 0);
+ } else {
+ obj = readRaw(store, 99, NAME2, 0, CASECLS, 0);
+ }
+ checkRawFields(obj, "key", 99, "ff", 88);
+ if (expectEvolved) {
+ if (expectUpdated) {
+ checkRawFields(obj.getSuper(), "f", 123);
+ } else {
+ checkRawFields(obj.getSuper());
+ }
+ checkRawFields(obj.getSuper().getSuper());
+ TestCase.assertNull(obj.getSuper().getSuper().getSuper());
+ } else {
+ checkRawFields(obj.getSuper());
+ TestCase.assertNull(obj.getSuper().getSuper());
+ }
+ }
+ }
+
+ @Persistent
+ static class InsertSuperclass2_Embedded_Base {
+
+ int g = 456;
+ }
+
+ @Persistent(version=1)
+ static class InsertSuperclass2_Embedded
+ extends InsertSuperclass2_Embedded_Base {
+
+ int f;
+ }
+
+ /**
+ * Allow inserting a superclass at the top of the hierarchy. No mutations
+ * are needed.
+ */
+ @Entity
+ static class InsertSuperclass2_Top
+ extends EvolveCase {
+
+ private static final String NAME =
+ InsertSuperclass2_Top.class.getName();
+ private static final String NAME2 =
+ InsertSuperclass2_Embedded.class.getName();
+ private static final String NAME3 =
+ InsertSuperclass2_Embedded_Base.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff;
+
+ InsertSuperclass2_Embedded embed =
+ new InsertSuperclass2_Embedded();
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkNonEntity(true, model, env, NAME2, 1);
+ checkNonEntity(true, model, env, NAME3, 0);
+ checkVersions(model, NAME, 0);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ checkVersions(model, NAME3, 0);
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertSuperclass2_Top>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ InsertSuperclass2_Top.class);
+ InsertSuperclass2_Top obj = index.get(key);
+ TestCase.assertNotNull(obj);
+ TestCase.assertNotNull(obj.embed);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.ff);
+ TestCase.assertEquals(123, obj.embed.f);
+ TestCase.assertEquals(456, obj.embed.g);
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertSuperclass2_Top>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ InsertSuperclass2_Top.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((InsertSuperclass2_Top)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawType embedType = store.getModel().getRawType(NAME2);
+ RawObject embedSuper = null;
+ if (expectEvolved) {
+ RawType embedSuperType = store.getModel().getRawType(NAME3);
+ Map<String,Object> values =
+ expectUpdated ? makeValues("g", 456) : makeValues();
+ embedSuper = new RawObject(embedSuperType, values, null);
+ }
+ RawObject embed =
+ new RawObject(embedType, makeValues("f", 123), embedSuper);
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88, "embed", embed);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_PrimitiveToObject
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_PrimitiveToObject.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ String ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_PrimitiveToObject version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_PrimitiveToObject version: 1 Error: Old field type: int is not compatible with the new type: java.lang.String for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_ObjectToPrimitive
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_ObjectToPrimitive.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_ObjectToPrimitive version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_ObjectToPrimitive version: 1 Error: Old field type: java.lang.String is not compatible with the new type: int for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", "88");
+ }
+ }
+
+ @Persistent
+ static class MyType {
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MyType;
+ }
+ }
+
+ @Persistent
+ static class MySubtype extends MyType {
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MySubtype;
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_ObjectToSubtype
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_ObjectToSubtype.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ MySubtype ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_ObjectToSubtype version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_ObjectToSubtype version: 1 Error: Old field type: com.sleepycat.persist.test.EvolveClasses$MyType is not compatible with the new type: com.sleepycat.persist.test.EvolveClasses$MySubtype for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawType embedType = store.getModel().getRawType
+ (MyType.class.getName());
+ RawObject embed = new RawObject(embedType, makeValues(), null);
+
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", embed);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_ObjectToUnrelatedSimple
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_ObjectToUnrelatedSimple.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ String ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_ObjectToUnrelatedSimple version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_ObjectToUnrelatedSimple version: 1 Error: Old field type: java.lang.Integer is not compatible with the new type: java.lang.String for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_ObjectToUnrelatedOther
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_ObjectToUnrelatedOther.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ MyType ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_ObjectToUnrelatedOther version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_ObjectToUnrelatedOther version: 1 Error: Old field type: java.lang.Integer is not compatible with the new type: com.sleepycat.persist.test.EvolveClasses$MyType for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_byte2boolean
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_byte2boolean.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ boolean ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_byte2boolean version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_byte2boolean version: 1 Error: Old field type: byte is not compatible with the new type: boolean for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (byte) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_short2byte
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_short2byte.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ byte ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_short2byte version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_short2byte version: 1 Error: Old field type: short is not compatible with the new type: byte for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (short) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_int2short
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_int2short.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ short ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_int2short version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_int2short version: 1 Error: Old field type: int is not compatible with the new type: short for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_long2int
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_long2int.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_long2int version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_long2int version: 1 Error: Old field type: long is not compatible with the new type: int for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (long) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_float2long
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_float2long.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ long ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_float2long version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_float2long version: 1 Error: Old field type: float is not compatible with the new type: long for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (float) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_double2float
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_double2float.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ float ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_double2float version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_double2float version: 1 Error: Old field type: double is not compatible with the new type: float for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (double) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_Byte2byte
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_Byte2byte.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ byte ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Byte2byte version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Byte2byte version: 1 Error: Old field type: java.lang.Byte is not compatible with the new type: byte for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (byte) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_Character2char
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_Character2char.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ char ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Character2char version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Character2char version: 1 Error: Old field type: java.lang.Character is not compatible with the new type: char for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (char) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_Short2short
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_Short2short.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ short ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Short2short version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Short2short version: 1 Error: Old field type: java.lang.Short is not compatible with the new type: short for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (short) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_Integer2int
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_Integer2int.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Integer2int version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Integer2int version: 1 Error: Old field type: java.lang.Integer is not compatible with the new type: int for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_Long2long
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_Long2long.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ long ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Long2long version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Long2long version: 1 Error: Old field type: java.lang.Long is not compatible with the new type: long for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (long) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_Float2float
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_Float2float.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ float ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Float2float version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Float2float version: 1 Error: Old field type: java.lang.Float is not compatible with the new type: float for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (float) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_Double2double
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_Double2double.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ double ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Double2double version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_Double2double version: 1 Error: Old field type: java.lang.Double is not compatible with the new type: double for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (double) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_float2BigInt
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_float2BigInt.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ BigInteger ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_float2BigInt version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_float2BigInt version: 1 Error: Old field type: float is not compatible with the new type: java.math.BigInteger for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (float) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowNonKeyField_BigInt2long
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowNonKeyField_BigInt2long.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ long ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_BigInt2long version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowNonKeyField_BigInt2long version: 1 Error: Old field type: java.math.BigInteger is not compatible with the new type: long for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", BigInteger.valueOf(88));
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_byte2short
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_byte2short.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ short ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_byte2short version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_byte2short version: 1 Error: Old field type: byte is not compatible with the new type: short for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (byte) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_char2int
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_char2int.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_char2int version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_char2int version: 1 Error: Old field type: char is not compatible with the new type: int for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (char) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_short2int
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_short2int.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_short2int version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_short2int version: 1 Error: Old field type: short is not compatible with the new type: int for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (short) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_int2long
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_int2long.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ long ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_int2long version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_int2long version: 1 Error: Old field type: int is not compatible with the new type: long for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_long2float
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_long2float.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ float ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_long2float version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_long2float version: 1 Error: Old field type: long is not compatible with the new type: float for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (long) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_float2double
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_float2double.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ double ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_float2double version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_float2double version: 1 Error: Old field type: float is not compatible with the new type: double for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (float) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_Byte2short2
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_Byte2short2.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ short ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Byte2short2 version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Byte2short2 version: 1 Error: Old field type: java.lang.Byte is not compatible with the new type: short for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (byte) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_Character2int
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_Character2int.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Character2int version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Character2int version: 1 Error: Old field type: java.lang.Character is not compatible with the new type: int for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (char) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_Short2int2
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_Short2int2.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Short2int2 version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Short2int2 version: 1 Error: Old field type: java.lang.Short is not compatible with the new type: int for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (short) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_Integer2long
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_Integer2long.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ long ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Integer2long version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Integer2long version: 1 Error: Old field type: java.lang.Integer is not compatible with the new type: long for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_Long2float2
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_Long2float2.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ float ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Long2float2 version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Long2float2 version: 1 Error: Old field type: java.lang.Long is not compatible with the new type: float for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (long) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_Float2double2
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_Float2double2.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ double ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Float2double2 version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_Float2double2 version: 1 Error: Old field type: java.lang.Float is not compatible with the new type: double for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", (float) 88);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowSecKeyField_int2BigInt
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowSecKeyField_int2BigInt.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ BigInteger ff;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_int2BigInt version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowSecKeyField_int2BigInt version: 1 Error: Old field type: int is not compatible with the new type: java.math.BigInteger for field: ff";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, "ff");
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "ff", 88);
+ }
+ }
+
+ // ---
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_byte2short
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_byte2short.class.getName();
+
+ @PrimaryKey
+ short key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_byte2short version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_byte2short version: 1 Error: Old field type: byte is not compatible with the new type: short for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (byte) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (byte) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_char2int
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_char2int.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_char2int version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_char2int version: 1 Error: Old field type: char is not compatible with the new type: int for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (char) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (char) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_short2int
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_short2int.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_short2int version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_short2int version: 1 Error: Old field type: short is not compatible with the new type: int for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (short) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (short) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_int2long
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_int2long.class.getName();
+
+ @PrimaryKey
+ long key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_int2long version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_int2long version: 1 Error: Old field type: int is not compatible with the new type: long for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_long2float
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_long2float.class.getName();
+
+ @PrimaryKey
+ float key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_long2float version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_long2float version: 1 Error: Old field type: long is not compatible with the new type: float for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (long) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (long) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_float2double
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_float2double.class.getName();
+
+ @PrimaryKey
+ double key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_float2double version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_float2double version: 1 Error: Old field type: float is not compatible with the new type: double for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (float) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (float) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_Byte2short2
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_Byte2short2.class.getName();
+
+ @PrimaryKey
+ short key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Byte2short2 version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Byte2short2 version: 1 Error: Old field type: java.lang.Byte is not compatible with the new type: short for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (byte) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (byte) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_Character2int
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_Character2int.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Character2int version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Character2int version: 1 Error: Old field type: java.lang.Character is not compatible with the new type: int for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (char) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (char) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_Short2int2
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_Short2int2.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Short2int2 version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Short2int2 version: 1 Error: Old field type: java.lang.Short is not compatible with the new type: int for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (short) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (short) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_Integer2long
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_Integer2long.class.getName();
+
+ @PrimaryKey
+ long key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Integer2long version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Integer2long version: 1 Error: Old field type: java.lang.Integer is not compatible with the new type: long for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_Long2float2
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_Long2float2.class.getName();
+
+ @PrimaryKey
+ float key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Long2float2 version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Long2float2 version: 1 Error: Old field type: java.lang.Long is not compatible with the new type: float for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (long) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (long) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_Float2double2
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_Float2double2.class.getName();
+
+ @PrimaryKey
+ double key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Float2double2 version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Float2double2 version: 1 Error: Old field type: java.lang.Float is not compatible with the new type: double for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, (float) 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", (float) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class DisallowPriKeyField_Long2BigInt
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowPriKeyField_Long2BigInt.class.getName();
+
+ @PrimaryKey
+ BigInteger key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Long2BigInt version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowPriKeyField_Long2BigInt version: 1 Error: Old field type: java.lang.Long is not compatible with the new type: java.math.BigInteger for field: key";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawObject obj = readRaw(store, 99L, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99L);
+ }
+ }
+
+ @Persistent(version=1)
+ static class DisallowCompositeKeyField_byte2short_Key {
+
+ @KeyField(1)
+ int f1 = 1;
+
+ @KeyField(2)
+ short f2 = 2;
+
+ @KeyField(3)
+ String f3 = "3";
+ }
+
+ @Entity
+ static class DisallowCompositeKeyField_byte2short
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowCompositeKeyField_byte2short.class.getName();
+ private static final String NAME2 =
+ DisallowCompositeKeyField_byte2short_Key.class.getName();
+
+ @PrimaryKey
+ DisallowCompositeKeyField_byte2short_Key key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Type may not be changed for a primary key field or composite key class field when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowCompositeKeyField_byte2short_Key version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowCompositeKeyField_byte2short_Key version: 1 Error: Old field type: byte is not compatible with the new type: short for field: f2";
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkNonEntity(true, model, env, NAME2, 0);
+ checkVersions(model, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ if (expectEvolved) {
+ TestCase.fail();
+ }
+ RawType rawKeyType = store.getModel().getRawType(NAME2);
+ RawObject rawKey = new RawObject
+ (rawKeyType,
+ makeValues("f1", 1, "f2", (byte) 2, "f3", "3"),
+ null);
+
+ RawObject obj = readRaw(store, rawKey, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", rawKey);
+ }
+ }
+
+ @Entity(version=1)
+ static class AllowPriKeyField_byte2Byte
+ extends EvolveCase {
+
+ private static final String NAME =
+ AllowPriKeyField_byte2Byte.class.getName();
+
+ @PrimaryKey
+ Byte key = 99;
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Byte,AllowPriKeyField_byte2Byte>
+ index = store.getPrimaryIndex
+ (Byte.class,
+ AllowPriKeyField_byte2Byte.class);
+ AllowPriKeyField_byte2Byte obj = index.get(key);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(Byte.valueOf((byte) 99), obj.key);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Byte,AllowPriKeyField_byte2Byte>
+ index = newStore.getPrimaryIndex
+ (Byte.class,
+ AllowPriKeyField_byte2Byte.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get((byte) 99);
+ index.put((AllowPriKeyField_byte2Byte)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj;
+ if (expectEvolved) {
+ obj = readRaw(store, (byte) 99, NAME, 1, CASECLS, 0);
+ } else {
+ obj = readRaw(store, (byte) 99, NAME, 0, CASECLS, 0);
+ }
+ checkRawFields(obj, "key", (byte) 99);
+ }
+ }
+
+ @Entity(version=1)
+ static class AllowPriKeyField_Byte2byte2
+ extends EvolveCase {
+
+ private static final String NAME =
+ AllowPriKeyField_Byte2byte2.class.getName();
+
+ @PrimaryKey
+ byte key = 99;
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Byte,AllowPriKeyField_Byte2byte2>
+ index = store.getPrimaryIndex
+ (Byte.class,
+ AllowPriKeyField_Byte2byte2.class);
+ AllowPriKeyField_Byte2byte2 obj = index.get(key);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals((byte) 99, obj.key);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Byte,AllowPriKeyField_Byte2byte2>
+ index = newStore.getPrimaryIndex
+ (Byte.class,
+ AllowPriKeyField_Byte2byte2.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get((byte) 99);
+ index.put((AllowPriKeyField_Byte2byte2)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj;
+ if (expectEvolved) {
+ obj = readRaw(store, (byte) 99, NAME, 1, CASECLS, 0);
+ } else {
+ obj = readRaw(store, (byte) 99, NAME, 0, CASECLS, 0);
+ }
+ checkRawFields(obj, "key", (byte) 99);
+ }
+ }
+
+ @Persistent(version=1)
+ static class AllowFieldTypeChanges_Key {
+
+ AllowFieldTypeChanges_Key() {
+ this(false);
+ }
+
+ AllowFieldTypeChanges_Key(boolean init) {
+ if (init) {
+ f1 = true;
+ f2 = (byte) 2;
+ f3 = (short) 3;
+ f4 = 4;
+ f5 = 5L;
+ f6 = 6F;
+ f7 = 7D;
+ f8 = (char) 8;
+ f9 = true;
+ f10 = (byte) 10;
+ f11 = (short) 11;
+ f12 = 12;
+ f13 = 13L;
+ f14 = 14F;
+ f15 = 15D;
+ f16 = (char) 16;
+ }
+ }
+
+ @KeyField(1)
+ boolean f1;
+
+ @KeyField(2)
+ byte f2;
+
+ @KeyField(3)
+ short f3;
+
+ @KeyField(4)
+ int f4;
+
+ @KeyField(5)
+ long f5;
+
+ @KeyField(6)
+ float f6;
+
+ @KeyField(7)
+ double f7;
+
+ @KeyField(8)
+ char f8;
+
+ @KeyField(9)
+ Boolean f9;
+
+ @KeyField(10)
+ Byte f10;
+
+ @KeyField(11)
+ Short f11;
+
+ @KeyField(12)
+ Integer f12;
+
+ @KeyField(13)
+ Long f13;
+
+ @KeyField(14)
+ Float f14;
+
+ @KeyField(15)
+ Double f15;
+
+ @KeyField(16)
+ Character f16;
+ }
+
+ @Persistent(version=1)
+ static class AllowFieldTypeChanges_Base
+ extends EvolveCase {
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ AllowFieldTypeChanges_Key kComposite;
+
+ Integer f_long2Integer;
+ Long f_String2Long;
+ }
+
+ /**
+ * Allow field type changes: automatic widening, supported widening,
+ * and Converter mutations. Also tests primary and secondary key field
+ * renaming.
+ */
+ @Entity(version=1)
+ static class AllowFieldTypeChanges
+ extends AllowFieldTypeChanges_Base {
+
+ private static final String NAME =
+ AllowFieldTypeChanges.class.getName();
+ private static final String NAME2 =
+ AllowFieldTypeChanges_Base.class.getName();
+ private static final String NAME3 =
+ AllowFieldTypeChanges_Key.class.getName();
+
+ @PrimaryKey
+ Integer pkeyInteger;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Boolean kBoolean;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Byte kByte;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Short kShort;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Integer kInteger;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Long kLong;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Float kFloat;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Double kDouble;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Character kCharacter;
+
+ short f01;
+ int f02;
+ long f03;
+ float f04;
+ double f06;
+ int f07;
+ long f08;
+ float f09;
+ double f10;
+ int f11;
+ long f12;
+ float f13;
+ double f14;
+ long f15;
+ float f16;
+ double f17;
+ float f18;
+ double f19;
+ double f20;
+
+ Short f21;
+ Integer f22;
+ Long f23;
+ Float f24;
+ Double f26;
+ Integer f27;
+ Long f28;
+ Float f29;
+ Double f30;
+ Integer f31;
+ Long f32;
+ Float f33;
+ Double f34;
+ Long f35;
+ Float f36;
+ Double f37;
+ Float f38;
+ Double f39;
+ Double f40;
+
+ Short f41;
+ Integer f42;
+ Long f43;
+ Float f44;
+ Double f46;
+ Integer f47;
+ Long f48;
+ Float f49;
+ Double f50;
+ Integer f51;
+ Long f52;
+ Float f53;
+ Double f54;
+ Long f55;
+ Float f56;
+ Double f57;
+ Float f58;
+ Double f59;
+ Double f60;
+
+ BigInteger f70;
+ BigInteger f71;
+ BigInteger f72;
+ BigInteger f73;
+ BigInteger f74;
+ BigInteger f75;
+ BigInteger f76;
+ BigInteger f77;
+ BigInteger f78;
+ BigInteger f79;
+
+ int f_long2int;
+ long f_String2long;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addRenamer(new Renamer(NAME, 0, "pkeyint", "pkeyInteger"));
+ m.addRenamer(new Renamer(NAME, 0, "kboolean", "kBoolean"));
+ m.addRenamer(new Renamer(NAME, 0, "kbyte", "kByte"));
+ m.addRenamer(new Renamer(NAME, 0, "kshort", "kShort"));
+ m.addRenamer(new Renamer(NAME, 0, "kint", "kInteger"));
+ m.addRenamer(new Renamer(NAME, 0, "klong", "kLong"));
+ m.addRenamer(new Renamer(NAME, 0, "kfloat", "kFloat"));
+ m.addRenamer(new Renamer(NAME, 0, "kdouble", "kDouble"));
+ m.addRenamer(new Renamer(NAME, 0, "kchar", "kCharacter"));
+ m.addRenamer(new Renamer(NAME2, 0, "kcomposite", "kComposite"));
+
+ Conversion conv1 = new MyConversion1();
+ Conversion conv2 = new MyConversion2();
+
+ m.addConverter(new Converter(NAME, 0, "f_long2int", conv1));
+ m.addConverter(new Converter(NAME, 0, "f_String2long", conv2));
+ m.addConverter(new Converter(NAME2, 0, "f_long2Integer", conv1));
+ m.addConverter(new Converter(NAME2, 0, "f_String2Long", conv2));
+ return m;
+ }
+
+ @SuppressWarnings("serial")
+ static class MyConversion1 implements Conversion {
+
+ public void initialize(EntityModel model) {}
+
+ public Object convert(Object o) {
+ return ((Long) o).intValue();
+ }
+
+ @Override
+ public boolean equals(Object other) { return true; }
+ }
+
+ @SuppressWarnings("serial")
+ static class MyConversion2 implements Conversion {
+
+ public void initialize(EntityModel model) {}
+
+ public Object convert(Object o) {
+ return Long.valueOf((String) o);
+ }
+
+ @Override
+ public boolean equals(Object other) { return true; }
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ checkNonEntity(true, model, env, NAME2, 1);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ checkVersions(model, NAME3, 1, NAME3, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ checkVersions(model, NAME2, 1);
+ checkVersions(model, NAME3, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowFieldTypeChanges>
+ index = store.getPrimaryIndex
+ (Integer.class, AllowFieldTypeChanges.class);
+ AllowFieldTypeChanges obj = index.get(99);
+ checkValues(obj);
+ checkSecondaries(store, index);
+
+ if (doUpdate) {
+ index.put(obj);
+ checkSecondaries(store, index);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowFieldTypeChanges>
+ index = newStore.getPrimaryIndex
+ (Integer.class, AllowFieldTypeChanges.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((AllowFieldTypeChanges)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ private void checkSecondaries(EntityStore store,
+ PrimaryIndex<Integer,
+ AllowFieldTypeChanges>
+ index)
+ throws DatabaseException {
+
+ checkValues(store.getSecondaryIndex
+ (index, Boolean.class, "kBoolean").get(true));
+ checkValues(store.getSecondaryIndex
+ (index, Byte.class, "kByte").get((byte) 77));
+ checkValues(store.getSecondaryIndex
+ (index, Short.class, "kShort").get((short) 66));
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "kInteger").get(55));
+ checkValues(store.getSecondaryIndex
+ (index, Long.class, "kLong").get((long) 44));
+ checkValues(store.getSecondaryIndex
+ (index, Float.class, "kFloat").get((float) 33));
+ checkValues(store.getSecondaryIndex
+ (index, Double.class, "kDouble").get((double) 22));
+ checkValues(store.getSecondaryIndex
+ (index, Character.class, "kCharacter").get((char) 11));
+ checkValues(store.getSecondaryIndex
+ (index, AllowFieldTypeChanges_Key.class, "kComposite").get
+ (new AllowFieldTypeChanges_Key(true)));
+ }
+
+ private void checkValues(AllowFieldTypeChanges obj) {
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(obj.pkeyInteger, Integer.valueOf(99));
+ TestCase.assertEquals(obj.kBoolean, Boolean.valueOf(true));
+ TestCase.assertEquals(obj.kByte, Byte.valueOf((byte) 77));
+ TestCase.assertEquals(obj.kShort, Short.valueOf((short) 66));
+ TestCase.assertEquals(obj.kInteger, Integer.valueOf(55));
+ TestCase.assertEquals(obj.kLong, Long.valueOf(44));
+ TestCase.assertEquals(obj.kFloat, Float.valueOf(33));
+ TestCase.assertEquals(obj.kDouble, Double.valueOf(22));
+ TestCase.assertEquals(obj.kCharacter, Character.valueOf((char) 11));
+
+ AllowFieldTypeChanges_Key embed = obj.kComposite;
+ TestCase.assertNotNull(embed);
+ TestCase.assertEquals(embed.f1, true);
+ TestCase.assertEquals(embed.f2, (byte) 2);
+ TestCase.assertEquals(embed.f3, (short) 3);
+ TestCase.assertEquals(embed.f4, 4);
+ TestCase.assertEquals(embed.f5, 5L);
+ TestCase.assertEquals(embed.f6, 6F);
+ TestCase.assertEquals(embed.f7, 7D);
+ TestCase.assertEquals(embed.f8, (char) 8);
+ TestCase.assertEquals(embed.f9, Boolean.valueOf(true));
+ TestCase.assertEquals(embed.f10, Byte.valueOf((byte) 10));
+ TestCase.assertEquals(embed.f11, Short.valueOf((short) 11));
+ TestCase.assertEquals(embed.f12, Integer.valueOf(12));
+ TestCase.assertEquals(embed.f13, Long.valueOf(13L));
+ TestCase.assertEquals(embed.f14, Float.valueOf(14F));
+ TestCase.assertEquals(embed.f15, Double.valueOf(15D));
+ TestCase.assertEquals(embed.f16, Character.valueOf((char) 16));
+
+ TestCase.assertEquals(obj.f01, (short) 1);
+ TestCase.assertEquals(obj.f02, 2);
+ TestCase.assertEquals(obj.f03, 3);
+ TestCase.assertEquals(obj.f04, (float) 4);
+ TestCase.assertEquals(obj.f06, (double) 6);
+ TestCase.assertEquals(obj.f07, 7);
+ TestCase.assertEquals(obj.f08, 8);
+ TestCase.assertEquals(obj.f09, (float) 9);
+ TestCase.assertEquals(obj.f10, (double) 10);
+ TestCase.assertEquals(obj.f11, 11);
+ TestCase.assertEquals(obj.f12, 12);
+ TestCase.assertEquals(obj.f13, (float) 13);
+ TestCase.assertEquals(obj.f14, (double) 14);
+ TestCase.assertEquals(obj.f15, 15L);
+ TestCase.assertEquals(obj.f16, 16F);
+ TestCase.assertEquals(obj.f17, 17D);
+ TestCase.assertEquals(obj.f18, (float) 18);
+ TestCase.assertEquals(obj.f19, (double) 19);
+ TestCase.assertEquals(obj.f20, (double) 20);
+
+ TestCase.assertEquals(obj.f21, Short.valueOf((byte) 21));
+ TestCase.assertEquals(obj.f22, Integer.valueOf((byte) 22));
+ TestCase.assertEquals(obj.f23, Long.valueOf((byte) 23));
+ TestCase.assertEquals(obj.f24, Float.valueOf((byte) 24));
+ TestCase.assertEquals(obj.f26, Double.valueOf((byte) 26));
+ TestCase.assertEquals(obj.f27, Integer.valueOf((short) 27));
+ TestCase.assertEquals(obj.f28, Long.valueOf((short) 28));
+ TestCase.assertEquals(obj.f29, Float.valueOf((short) 29));
+ TestCase.assertEquals(obj.f30, Double.valueOf((short) 30));
+ TestCase.assertEquals(obj.f31, Integer.valueOf((char) 31));
+ TestCase.assertEquals(obj.f32, Long.valueOf((char) 32));
+ TestCase.assertEquals(obj.f33, Float.valueOf((char) 33));
+ TestCase.assertEquals(obj.f34, Double.valueOf((char) 34));
+ TestCase.assertEquals(obj.f35, Long.valueOf(35));
+ TestCase.assertEquals(obj.f36, Float.valueOf(36));
+ TestCase.assertEquals(obj.f37, Double.valueOf(37));
+ TestCase.assertEquals(obj.f38, Float.valueOf(38));
+ TestCase.assertEquals(obj.f39, Double.valueOf(39));
+ TestCase.assertEquals(obj.f40, Double.valueOf(40));
+
+ TestCase.assertEquals(obj.f41, Short.valueOf((byte) 41));
+ TestCase.assertEquals(obj.f42, Integer.valueOf((byte) 42));
+ TestCase.assertEquals(obj.f43, Long.valueOf((byte) 43));
+ TestCase.assertEquals(obj.f44, Float.valueOf((byte) 44));
+ TestCase.assertEquals(obj.f46, Double.valueOf((byte) 46));
+ TestCase.assertEquals(obj.f47, Integer.valueOf((short) 47));
+ TestCase.assertEquals(obj.f48, Long.valueOf((short) 48));
+ TestCase.assertEquals(obj.f49, Float.valueOf((short) 49));
+ TestCase.assertEquals(obj.f50, Double.valueOf((short) 50));
+ TestCase.assertEquals(obj.f51, Integer.valueOf((char) 51));
+ TestCase.assertEquals(obj.f52, Long.valueOf((char) 52));
+ TestCase.assertEquals(obj.f53, Float.valueOf((char) 53));
+ TestCase.assertEquals(obj.f54, Double.valueOf((char) 54));
+ TestCase.assertEquals(obj.f55, Long.valueOf(55));
+ TestCase.assertEquals(obj.f56, Float.valueOf(56));
+ TestCase.assertEquals(obj.f57, Double.valueOf(57));
+ TestCase.assertEquals(obj.f58, Float.valueOf(58));
+ TestCase.assertEquals(obj.f59, Double.valueOf(59));
+ TestCase.assertEquals(obj.f60, Double.valueOf(60));
+
+ TestCase.assertEquals(obj.f70, BigInteger.valueOf(70));
+ TestCase.assertEquals(obj.f71, BigInteger.valueOf(71));
+ TestCase.assertEquals(obj.f72, BigInteger.valueOf(72));
+ TestCase.assertEquals(obj.f73, BigInteger.valueOf(73));
+ TestCase.assertEquals(obj.f74, BigInteger.valueOf(74));
+ TestCase.assertEquals(obj.f75, BigInteger.valueOf(75));
+ TestCase.assertEquals(obj.f76, BigInteger.valueOf(76));
+ TestCase.assertEquals(obj.f77, BigInteger.valueOf(77));
+ TestCase.assertEquals(obj.f78, BigInteger.valueOf(78));
+ TestCase.assertEquals(obj.f79, BigInteger.valueOf(79));
+
+ TestCase.assertEquals(obj.f_long2Integer, Integer.valueOf(111));
+ TestCase.assertEquals(obj.f_String2Long, Long.valueOf(222));
+ TestCase.assertEquals(obj.f_long2int, 333);
+ TestCase.assertEquals(obj.f_String2long, 444L);
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawType embedType = store.getModel().getRawType(NAME3);
+ RawObject embed = new RawObject
+ (embedType,
+ makeValues
+ ("f1", true,
+ "f2", (byte) 2,
+ "f3", (short) 3,
+ "f4", 4,
+ "f5", 5L,
+ "f6", 6F,
+ "f7", 7D,
+ "f8", (char) 8,
+ "f9", true,
+ "f10", (byte) 10,
+ "f11", (short) 11,
+ "f12", 12,
+ "f13", 13L,
+ "f14", 14F,
+ "f15", 15D,
+ "f16", (char) 16),
+ null);
+
+ RawObject obj;
+ if (expectEvolved) {
+ obj = readRaw(store, 99, NAME, 1, NAME2, 1, CASECLS, 0);
+ checkRawFields(obj, "pkeyInteger", 99,
+ "kBoolean", true,
+ "kByte", (byte) 77,
+ "kShort", (short) 66,
+ "kInteger", 55,
+ "kLong", (long) 44,
+ "kFloat", (float) 33,
+ "kDouble", (double) 22,
+ "kCharacter", (char) 11,
+
+ "f01", (short) 1,
+ "f02", 2,
+ "f03", (long) 3,
+ "f04", (float) 4,
+ "f06", (double) 6,
+ "f07", 7,
+ "f08", (long) 8,
+ "f09", (float) 9,
+ "f10", (double) 10,
+ "f11", 11,
+ "f12", (long) 12,
+ "f13", (float) 13,
+ "f14", (double) 14,
+ "f15", 15L,
+ "f16", 16F,
+ "f17", 17D,
+ "f18", (float) 18,
+ "f19", (double) 19,
+ "f20", (double) 20,
+
+ "f21", (short) 21,
+ "f22", 22,
+ "f23", (long) 23,
+ "f24", (float) 24,
+ "f26", (double) 26,
+ "f27", 27,
+ "f28", (long) 28,
+ "f29", (float) 29,
+ "f30", (double) 30,
+ "f31", 31,
+ "f32", (long) 32,
+ "f33", (float) 33,
+ "f34", (double) 34,
+ "f35", 35L,
+ "f36", 36F,
+ "f37", 37D,
+ "f38", (float) 38,
+ "f39", (double) 39,
+ "f40", (double) 40,
+
+ "f41", (short) 41,
+ "f42", 42,
+ "f43", (long) 43,
+ "f44", (float) 44,
+ "f46", (double) 46,
+ "f47", 47,
+ "f48", (long) 48,
+ "f49", (float) 49,
+ "f50", (double) 50,
+ "f51", 51,
+ "f52", (long) 52,
+ "f53", (float) 53,
+ "f54", (double) 54,
+ "f55", 55L,
+ "f56", 56F,
+ "f57", 57D,
+ "f58", (float) 58,
+ "f59", (double) 59,
+ "f60", (double) 60,
+
+ "f70", BigInteger.valueOf(70),
+ "f71", BigInteger.valueOf(71),
+ "f72", BigInteger.valueOf(72),
+ "f73", BigInteger.valueOf(73),
+ "f74", BigInteger.valueOf(74),
+ "f75", BigInteger.valueOf(75),
+ "f76", BigInteger.valueOf(76),
+ "f77", BigInteger.valueOf(77),
+ "f78", BigInteger.valueOf(78),
+ "f79", BigInteger.valueOf(79),
+
+ "f_long2int", 333,
+ "f_String2long", 444L);
+ checkRawFields(obj.getSuper(),
+ "kComposite", embed,
+ "f_long2Integer", 111,
+ "f_String2Long", 222L);
+ } else {
+ obj = readRaw(store, 99, NAME, 0, NAME2, 0, CASECLS, 0);
+ checkRawFields(obj, "pkeyint", 99,
+ "kboolean", true,
+ "kbyte", (byte) 77,
+ "kshort", (short) 66,
+ "kint", 55,
+ "klong", (long) 44,
+ "kfloat", (float) 33,
+ "kdouble", (double) 22,
+ "kchar", (char) 11,
+
+ "f01", (byte) 1,
+ "f02", (byte) 2,
+ "f03", (byte) 3,
+ "f04", (byte) 4,
+ "f06", (byte) 6,
+ "f07", (short) 7,
+ "f08", (short) 8,
+ "f09", (short) 9,
+ "f10", (short) 10,
+ "f11", (char) 11,
+ "f12", (char) 12,
+ "f13", (char) 13,
+ "f14", (char) 14,
+ "f15", 15,
+ "f16", 16,
+ "f17", 17,
+ "f18", (long) 18,
+ "f19", (long) 19,
+ "f20", (float) 20,
+
+ "f21", (byte) 21,
+ "f22", (byte) 22,
+ "f23", (byte) 23,
+ "f24", (byte) 24,
+ "f26", (byte) 26,
+ "f27", (short) 27,
+ "f28", (short) 28,
+ "f29", (short) 29,
+ "f30", (short) 30,
+ "f31", (char) 31,
+ "f32", (char) 32,
+ "f33", (char) 33,
+ "f34", (char) 34,
+ "f35", 35,
+ "f36", 36,
+ "f37", 37,
+ "f38", (long) 38,
+ "f39", (long) 39,
+ "f40", (float) 40,
+
+ "f41", (byte) 41,
+ "f42", (byte) 42,
+ "f43", (byte) 43,
+ "f44", (byte) 44,
+ "f46", (byte) 46,
+ "f47", (short) 47,
+ "f48", (short) 48,
+ "f49", (short) 49,
+ "f50", (short) 50,
+ "f51", (char) 51,
+ "f52", (char) 52,
+ "f53", (char) 53,
+ "f54", (char) 54,
+ "f55", 55,
+ "f56", 56,
+ "f57", 57,
+ "f58", (long) 58,
+ "f59", (long) 59,
+ "f60", (float) 60,
+
+ "f70", (byte) 70,
+ "f71", (short) 71,
+ "f72", (char) 72,
+ "f73", 73,
+ "f74", (long) 74,
+ "f75", (byte) 75,
+ "f76", (short) 76,
+ "f77", (char) 77,
+ "f78", 78,
+ "f79", (long) 79,
+
+ "f_long2int", 333L,
+ "f_String2long", "444");
+
+ checkRawFields(obj.getSuper(),
+ "kcomposite", embed,
+ "f_long2Integer", 111L,
+ "f_String2Long", "222");
+ }
+ Environment env = store.getEnvironment();
+
+ assertDbExists(expectEvolved, env, NAME, "kBoolean");
+ assertDbExists(expectEvolved, env, NAME, "kByte");
+ assertDbExists(expectEvolved, env, NAME, "kShort");
+ assertDbExists(expectEvolved, env, NAME, "kInteger");
+ assertDbExists(expectEvolved, env, NAME, "kLong");
+ assertDbExists(expectEvolved, env, NAME, "kFloat");
+ assertDbExists(expectEvolved, env, NAME, "kDouble");
+ assertDbExists(expectEvolved, env, NAME, "kCharacter");
+ assertDbExists(expectEvolved, env, NAME, "kComposite");
+
+ assertDbExists(!expectEvolved, env, NAME, "kboolean");
+ assertDbExists(!expectEvolved, env, NAME, "kbyte");
+ assertDbExists(!expectEvolved, env, NAME, "kshort");
+ assertDbExists(!expectEvolved, env, NAME, "kint");
+ assertDbExists(!expectEvolved, env, NAME, "klong");
+ assertDbExists(!expectEvolved, env, NAME, "kfloat");
+ assertDbExists(!expectEvolved, env, NAME, "kdouble");
+ assertDbExists(!expectEvolved, env, NAME, "kchar");
+ assertDbExists(!expectEvolved, env, NAME, "kcomposite");
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static class ConvertFieldContent_Conversion implements Conversion {
+
+ public void initialize(EntityModel model) {
+ }
+
+ public Object convert(Object fromValue) {
+ String s1 = (String) fromValue;
+ return (new StringBuilder(s1)).reverse().toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ConvertFieldContent_Conversion;
+ }
+ }
+
+ @Entity(version=1)
+ static class ConvertFieldContent_Entity
+ extends EvolveCase {
+
+ private static final String NAME =
+ ConvertFieldContent_Entity.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ String f1;
+ String f2;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ Converter converter = new Converter
+ (ConvertFieldContent_Entity.class.getName(), 0,
+ "f1", new ConvertFieldContent_Conversion());
+ m.addConverter(converter);
+ converter = new Converter
+ (ConvertFieldContent_Entity.class.getName(), 0,
+ "f2", new ConvertFieldContent_Conversion());
+ m.addConverter(converter);
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertFieldContent_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ConvertFieldContent_Entity.class);
+ ConvertFieldContent_Entity obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals("43210", obj.f1);
+ TestCase.assertEquals("98765", obj.f2);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertFieldContent_Entity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ConvertFieldContent_Entity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ConvertFieldContent_Entity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj =
+ readRaw(store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ if (expectEvolved) {
+ checkRawFields(obj, "key", 99,
+ "f1", "43210",
+ "f2", "98765");
+ } else {
+ checkRawFields(obj, "key", 99,
+ "f1", "01234",
+ "f2", "56789");
+ }
+ }
+ }
+
+ @Persistent(version=1)
+ static class ConvertExample1_Address {
+ String street;
+ String city;
+ String state;
+ int zipCode;
+ }
+
+ @SuppressWarnings("serial")
+ static class ConvertExample1_Conversion implements Conversion {
+
+ public void initialize(EntityModel model) {
+ }
+
+ public Object convert(Object fromValue) {
+ return Integer.valueOf((String) fromValue);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ConvertExample1_Conversion;
+ }
+ }
+
+ @Entity
+ static class ConvertExample1_Entity
+ extends EvolveCase {
+
+ private static final String NAME =
+ ConvertExample1_Entity.class.getName();
+ private static final String NAME2 =
+ ConvertExample1_Address.class.getName();
+
+ @PrimaryKey
+ int key = 99;
+
+ ConvertExample1_Address embed;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ Converter converter = new Converter
+ (ConvertExample1_Address.class.getName(), 0,
+ "zipCode", new ConvertExample1_Conversion());
+ m.addConverter(converter);
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample1_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ConvertExample1_Entity.class);
+ ConvertExample1_Entity obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertNotNull(obj.embed);
+ TestCase.assertEquals("street", obj.embed.street);
+ TestCase.assertEquals("city", obj.embed.city);
+ TestCase.assertEquals("state", obj.embed.state);
+ TestCase.assertEquals(12345, obj.embed.zipCode);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample1_Entity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ConvertExample1_Entity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ConvertExample1_Entity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawType embedType = store.getModel().getRawType(NAME2);
+ RawObject embed;
+ if (expectEvolved) {
+ embed = new RawObject
+ (embedType,
+ makeValues("street", "street",
+ "city", "city",
+ "state", "state",
+ "zipCode", 12345),
+ null);
+ } else {
+ embed = new RawObject
+ (embedType,
+ makeValues("street", "street",
+ "city", "city",
+ "state", "state",
+ "zipCode", "12345"),
+ null);
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ @Persistent
+ static class ConvertExample2_Address {
+ String street;
+ String city;
+ String state;
+ int zipCode;
+ }
+
+ @Entity(version=1)
+ static class ConvertExample2_Person
+ extends EvolveCase {
+
+ private static final String NAME =
+ ConvertExample2_Person.class.getName();
+ private static final String NAME2 =
+ ConvertExample2_Address .class.getName();
+
+ @PrimaryKey
+ int key;
+
+ ConvertExample2_Address address;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ Converter converter = new Converter
+ (ConvertExample2_Person.class.getName(), 0,
+ "address", new ConvertExample2_Conversion());
+ m.addConverter(converter);
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ checkVersions(model, NAME2, 0);
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample2_Person>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ConvertExample2_Person.class);
+ ConvertExample2_Person obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertNotNull(obj.address);
+ TestCase.assertEquals("street", obj.address.street);
+ TestCase.assertEquals("city", obj.address.city);
+ TestCase.assertEquals("state", obj.address.state);
+ TestCase.assertEquals(12345, obj.address.zipCode);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample2_Person>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ConvertExample2_Person.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ConvertExample2_Person)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ Object embed;
+ if (expectEvolved) {
+ RawType embedType = store.getModel().getRawType(NAME2);
+ embed = new RawObject
+ (embedType,
+ makeValues("street", "street",
+ "city", "city",
+ "state", "state",
+ "zipCode", 12345),
+ null);
+ } else {
+ embed = "street#city#state#12345";
+ }
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "address", embed);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static class ConvertExample2_Conversion implements Conversion {
+ private transient RawType addressType;
+
+ public void initialize(EntityModel model) {
+ addressType = model.getRawType
+ (ConvertExample2_Address.class.getName());
+ }
+
+ public Object convert(Object fromValue) {
+
+ String oldAddress = (String) fromValue;
+ Map<String,Object> addressValues = new HashMap<String,Object>();
+ addressValues.put("street", parseAddress(1, oldAddress));
+ addressValues.put("city", parseAddress(2, oldAddress));
+ addressValues.put("state", parseAddress(3, oldAddress));
+ addressValues.put("zipCode",
+ Integer.valueOf(parseAddress(4, oldAddress)));
+
+ return new RawObject(addressType, addressValues, null);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ConvertExample2_Conversion;
+ }
+
+ private String parseAddress(int fieldNum, String oldAddress) {
+ StringTokenizer tokens = new StringTokenizer(oldAddress, "#");
+ String field = null;
+ for (int i = 0; i < fieldNum; i += 1) {
+ field = tokens.nextToken();
+ }
+ return field;
+ }
+ }
+
+ @Persistent
+ static class ConvertExample3_Address {
+ String street;
+ String city;
+ String state;
+ int zipCode;
+ }
+
+ @SuppressWarnings("serial")
+ static class ConvertExample3_Conversion implements Conversion {
+ private transient RawType newPersonType;
+ private transient RawType addressType;
+
+ public void initialize(EntityModel model) {
+ newPersonType = model.getRawType
+ (ConvertExample3_Person.class.getName());
+ addressType = model.getRawType
+ (ConvertExample3_Address.class.getName());
+ }
+
+ public Object convert(Object fromValue) {
+
+ RawObject person = (RawObject) fromValue;
+ Map<String,Object> personValues = person.getValues();
+ Map<String,Object> addressValues = new HashMap<String,Object>();
+ RawObject address = new RawObject
+ (addressType, addressValues, null);
+
+ addressValues.put("street", personValues.remove("street"));
+ addressValues.put("city", personValues.remove("city"));
+ addressValues.put("state", personValues.remove("state"));
+ addressValues.put("zipCode", personValues.remove("zipCode"));
+ personValues.put("address", address);
+
+ return new RawObject
+ (newPersonType, personValues, person.getSuper());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ConvertExample3_Conversion;
+ }
+ }
+
+ @Entity(version=1)
+ static class ConvertExample3_Person
+ extends EvolveCase {
+
+ private static final String NAME =
+ ConvertExample3_Person.class.getName();
+ private static final String NAME2 =
+ ConvertExample3_Address .class.getName();
+
+ @PrimaryKey
+ int key;
+
+ ConvertExample3_Address address;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ Converter converter = new Converter
+ (ConvertExample3_Person.class.getName(), 0,
+ new ConvertExample3_Conversion());
+ m.addConverter(converter);
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ checkVersions(model, NAME2, 0);
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample3_Person>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ConvertExample3_Person.class);
+ ConvertExample3_Person obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertNotNull(obj.address);
+ TestCase.assertEquals("street", obj.address.street);
+ TestCase.assertEquals("city", obj.address.city);
+ TestCase.assertEquals("state", obj.address.state);
+ TestCase.assertEquals(12345, obj.address.zipCode);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample3_Person>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ConvertExample3_Person.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ConvertExample3_Person)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ if (expectEvolved) {
+ RawType embedType = store.getModel().getRawType(NAME2);
+ Object embed = new RawObject
+ (embedType,
+ makeValues("street", "street",
+ "city", "city",
+ "state", "state",
+ "zipCode", 12345),
+ null);
+ checkRawFields(obj, "key", 99, "address", embed);
+ } else {
+ checkRawFields(obj, "key", 99,
+ "street", "street",
+ "city", "city",
+ "state", "state",
+ "zipCode", 12345);
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static class ConvertExample3Reverse_Conversion implements Conversion {
+ private transient RawType newPersonType;
+
+ public void initialize(EntityModel model) {
+ newPersonType = model.getRawType
+ (ConvertExample3Reverse_Person.class.getName());
+ }
+
+ public Object convert(Object fromValue) {
+
+ RawObject person = (RawObject) fromValue;
+ Map<String,Object> personValues = person.getValues();
+ RawObject address = (RawObject) personValues.remove("address");
+ Map<String,Object> addressValues = address.getValues();
+
+ personValues.put("street", addressValues.remove("street"));
+ personValues.put("city", addressValues.remove("city"));
+ personValues.put("state", addressValues.remove("state"));
+ personValues.put("zipCode", addressValues.remove("zipCode"));
+
+ return new RawObject
+ (newPersonType, personValues, person.getSuper());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ConvertExample3Reverse_Conversion;
+ }
+ }
+
+ @Entity(version=1)
+ static class ConvertExample3Reverse_Person
+ extends EvolveCase {
+
+ private static final String NAME =
+ ConvertExample3Reverse_Person.class.getName();
+ private static final String NAME2 =
+ PREFIX + "ConvertExample3Reverse_Address";
+
+ @PrimaryKey
+ int key;
+
+ String street;
+ String city;
+ String state;
+ int zipCode;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ Converter converter = new Converter
+ (ConvertExample3Reverse_Person.class.getName(), 0,
+ new ConvertExample3Reverse_Conversion());
+ m.addConverter(converter);
+ m.addDeleter(new Deleter(NAME2, 0));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample3Reverse_Person>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ConvertExample3Reverse_Person.class);
+ ConvertExample3Reverse_Person obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals("street", obj.street);
+ TestCase.assertEquals("city", obj.city);
+ TestCase.assertEquals("state", obj.state);
+ TestCase.assertEquals(12345, obj.zipCode);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample3Reverse_Person>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ConvertExample3Reverse_Person.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ConvertExample3Reverse_Person)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ if (expectEvolved) {
+ checkRawFields(obj, "key", 99,
+ "street", "street",
+ "city", "city",
+ "state", "state",
+ "zipCode", 12345);
+ } else {
+ RawType embedType = store.getModel().getRawType(NAME2);
+ Object embed = new RawObject
+ (embedType,
+ makeValues("street", "street",
+ "city", "city",
+ "state", "state",
+ "zipCode", 12345),
+ null);
+ checkRawFields(obj, "key", 99, "address", embed);
+ }
+ }
+ }
+
+ @Persistent(version=1)
+ static class ConvertExample4_A extends ConvertExample4_B {
+ }
+
+ @Persistent(version=1)
+ static class ConvertExample4_B {
+ String name;
+ }
+
+ @SuppressWarnings("serial")
+ static class Example4_Conversion implements Conversion {
+ private transient RawType newAType;
+ private transient RawType newBType;
+
+ public void initialize(EntityModel model) {
+ newAType = model.getRawType(ConvertExample4_A.class.getName());
+ newBType = model.getRawType(ConvertExample4_B.class.getName());
+ }
+
+ public Object convert(Object fromValue) {
+ RawObject oldA = (RawObject) fromValue;
+ RawObject oldB = oldA.getSuper();
+ Map<String,Object> aValues = oldA.getValues();
+ Map<String,Object> bValues = oldB.getValues();
+ bValues.put("name", aValues.remove("name"));
+ RawObject newB = new RawObject(newBType, bValues, oldB.getSuper());
+ RawObject newA = new RawObject(newAType, aValues, newB);
+ return newA;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof Example4_Conversion;
+ }
+ }
+
+ @Entity(version=1)
+ static class ConvertExample4_Entity
+ extends EvolveCase {
+
+ private static final String NAME =
+ ConvertExample4_Entity.class.getName();
+ private static final String NAME2 =
+ ConvertExample4_A .class.getName();
+ private static final String NAME3 =
+ ConvertExample4_B .class.getName();
+
+ @PrimaryKey
+ int key;
+
+ ConvertExample4_A embed;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ Converter converter = new Converter
+ (ConvertExample4_A.class.getName(), 0,
+ new Example4_Conversion());
+ m.addConverter(converter);
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ checkVersions(model, NAME3, 1, NAME3, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ checkVersions(model, NAME2, 1);
+ checkVersions(model, NAME3, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample4_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ConvertExample4_Entity.class);
+ ConvertExample4_Entity obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertNotNull(obj.embed);
+ TestCase.assertEquals("name", obj.embed.name);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample4_Entity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ConvertExample4_Entity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ConvertExample4_Entity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawType embedTypeA = store.getModel().getRawType(NAME2);
+ RawType embedTypeB = store.getModel().getRawType(NAME3);
+ Object embed;
+ if (expectEvolved) {
+ embed = new RawObject(embedTypeA, makeValues(),
+ new RawObject
+ (embedTypeB, makeValues("name", "name"), null));
+ } else {
+ embed = new RawObject(embedTypeA, makeValues("name", "name"),
+ new RawObject
+ (embedTypeB, makeValues(), null));
+ }
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ @Persistent(version=1)
+ static class ConvertExample5_Pet {
+ String name;
+ }
+
+ @Persistent
+ static class ConvertExample5_Cat extends ConvertExample5_Pet {
+ int finickyLevel;
+ }
+
+ @Persistent
+ static class ConvertExample5_Dog extends ConvertExample5_Pet {
+ double barkVolume;
+ }
+
+ @SuppressWarnings("serial")
+ static class ConvertExample5_Conversion implements Conversion {
+ private transient RawType newPetType;
+ private transient RawType dogType;
+ private transient RawType catType;
+
+ public void initialize(EntityModel model) {
+ newPetType = model.getRawType(ConvertExample5_Pet.class.getName());
+ dogType = model.getRawType(ConvertExample5_Dog.class.getName());
+ catType = model.getRawType(ConvertExample5_Cat.class.getName());
+ }
+
+ public Object convert(Object fromValue) {
+ RawObject pet = (RawObject) fromValue;
+ Map<String,Object> petValues = pet.getValues();
+ Map<String,Object> subTypeValues = new HashMap<String,Object>();
+ Boolean isCat = (Boolean) petValues.remove("isCatNotDog");
+ Integer finickyLevel = (Integer) petValues.remove("finickyLevel");
+ Double barkVolume = (Double) petValues.remove("barkVolume");
+ RawType newSubType;
+ if (isCat) {
+ newSubType = catType;
+ subTypeValues.put("finickyLevel", finickyLevel);
+ } else {
+ newSubType = dogType;
+ subTypeValues.put("barkVolume", barkVolume);
+ }
+ RawObject newPet = new RawObject
+ (newPetType, petValues, pet.getSuper());
+ return new RawObject(newSubType, subTypeValues, newPet);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ConvertExample5_Conversion;
+ }
+ }
+
+ @Entity(version=1)
+ static class ConvertExample5_Entity
+ extends EvolveCase {
+
+ private static final String NAME =
+ ConvertExample5_Entity.class.getName();
+ private static final String NAME2 =
+ ConvertExample5_Pet.class.getName();
+ private static final String NAME3 =
+ ConvertExample5_Cat.class.getName();
+ private static final String NAME4 =
+ ConvertExample5_Dog.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ ConvertExample5_Cat cat;
+ ConvertExample5_Dog dog;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ Converter converter = new Converter
+ (ConvertExample5_Pet.class.getName(), 0,
+ new ConvertExample5_Conversion());
+ m.addConverter(converter);
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ checkVersions(model, NAME2, 1);
+ }
+ checkVersions(model, NAME3, 0);
+ checkVersions(model, NAME4, 0);
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample5_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ConvertExample5_Entity.class);
+ ConvertExample5_Entity obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertNotNull(obj.cat);
+ TestCase.assertEquals("Jeffry", obj.cat.name);
+ TestCase.assertEquals(999, obj.cat.finickyLevel);
+ TestCase.assertNotNull(obj.dog);
+ TestCase.assertEquals("Nelson", obj.dog.name);
+ TestCase.assertEquals(0.01, obj.dog.barkVolume);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample5_Entity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ConvertExample5_Entity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ConvertExample5_Entity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawType petType = store.getModel().getRawType(NAME2);
+ RawObject cat;
+ RawObject dog;
+ if (expectEvolved) {
+ RawType catType = store.getModel().getRawType(NAME3);
+ RawType dogType = store.getModel().getRawType(NAME4);
+ cat = new RawObject(catType, makeValues("finickyLevel", 999),
+ new RawObject(petType, makeValues("name", "Jeffry"),
+ null));
+ dog = new RawObject(dogType, makeValues("barkVolume", 0.01),
+ new RawObject(petType, makeValues("name", "Nelson"),
+ null));
+ } else {
+ cat = new RawObject(petType, makeValues("name", "Jeffry",
+ "isCatNotDog", true,
+ "finickyLevel", 999,
+ "barkVolume", 0.0),
+ null);
+ dog = new RawObject(petType, makeValues("name", "Nelson",
+ "isCatNotDog", false,
+ "finickyLevel", 0,
+ "barkVolume", 0.01),
+ null);
+ }
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "cat", cat, "dog", dog);
+ }
+ }
+
+ @Persistent(version=1)
+ static class AllowFieldAddDelete_Embed {
+ private final String f0 = "0";
+ private String f2;
+ private final int f3 = 3;
+ private String f4;
+ private final int f5 = 5;
+ private final String f8 = "8";
+ private final int f9 = 9;
+ }
+
+ @Persistent(version=1)
+ static class AllowFieldAddDelete_Base
+ extends EvolveCase {
+
+ private final String f0 = "0";
+ private String f2;
+ private final int f3 = 3;
+ private String f4;
+ private final int f5 = 5;
+ private final String f8 = "8";
+ private final int f9 = 9;
+ }
+
+ @Entity(version=1)
+ static class AllowFieldAddDelete
+ extends AllowFieldAddDelete_Base {
+
+ private static final String NAME =
+ AllowFieldAddDelete.class.getName();
+ private static final String NAME2 =
+ AllowFieldAddDelete_Base.class.getName();
+ private static final String NAME3 =
+ AllowFieldAddDelete_Embed.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ AllowFieldAddDelete_Embed embed;
+
+ private final String f0 = "0";
+ private String f2;
+ private final int f3 = 3;
+ private String f4;
+ private final int f5 = 5;
+ private final String f8 = "8";
+ private final int f9 = 9;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ for (String name : new String[] {NAME, NAME2, NAME3}) {
+ m.addDeleter(new Deleter(name, 0, "f1"));
+ m.addDeleter(new Deleter(name, 0, "f6"));
+ m.addDeleter(new Deleter(name, 0, "f7"));
+ }
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ checkVersions(model, NAME3, 1, NAME3, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ checkVersions(model, NAME2, 1);
+ checkVersions(model, NAME3, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowFieldAddDelete>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ AllowFieldAddDelete.class);
+ AllowFieldAddDelete obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ {
+ AllowFieldAddDelete o = obj;
+
+ TestCase.assertNotNull(o);
+ TestCase.assertEquals("0", o.f0);
+ TestCase.assertEquals("2", o.f2);
+ TestCase.assertEquals(3, o.f3);
+ TestCase.assertEquals("4", o.f4);
+ TestCase.assertEquals(5, o.f5);
+ TestCase.assertEquals("8", o.f8);
+ TestCase.assertEquals(9, o.f9);
+ }
+ {
+ AllowFieldAddDelete_Base o = obj;
+
+ TestCase.assertNotNull(o);
+ TestCase.assertEquals("0", o.f0);
+ TestCase.assertEquals("2", o.f2);
+ TestCase.assertEquals(3, o.f3);
+ TestCase.assertEquals("4", o.f4);
+ TestCase.assertEquals(5, o.f5);
+ TestCase.assertEquals("8", o.f8);
+ TestCase.assertEquals(9, o.f9);
+ }
+ {
+ AllowFieldAddDelete_Embed o = obj.embed;
+
+ TestCase.assertNotNull(o);
+ TestCase.assertEquals("0", o.f0);
+ TestCase.assertEquals("2", o.f2);
+ TestCase.assertEquals(3, o.f3);
+ TestCase.assertEquals("4", o.f4);
+ TestCase.assertEquals(5, o.f5);
+ TestCase.assertEquals("8", o.f8);
+ TestCase.assertEquals(9, o.f9);
+ }
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowFieldAddDelete>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ AllowFieldAddDelete.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((AllowFieldAddDelete)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ static final Object[] fixedFields0 = {
+ "f1", 1,
+ "f2", "2",
+ "f4", "4",
+ "f6", 6,
+ "f7", "7",
+ };
+
+ static final Object[] fixedFields1 = {
+ "f2", "2",
+ "f4", "4",
+ };
+
+ static final Object[] fixedFields2 = {
+ "f0", "0",
+ "f2", "2",
+ "f3", 3,
+ "f4", "4",
+ "f5", 5,
+ "f8", "8",
+ "f9", 9,
+ };
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawType baseType = store.getModel().getRawType(NAME2);
+ RawType embedType = store.getModel().getRawType(NAME3);
+
+ Object[] ff;
+ if (expectEvolved) {
+ if (expectUpdated) {
+ ff = fixedFields2;
+ } else {
+ ff = fixedFields1;
+ }
+ } else {
+ ff = fixedFields0;
+ }
+ RawObject embed = new RawObject(embedType, makeValues(ff), null);
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0,
+ NAME2, expectEvolved ? 1 : 0,
+ CASECLS, 0);
+ checkRaw(obj, ff, "key", 99, "embed", embed);
+ checkRaw(obj.getSuper(), ff);
+ }
+
+ private void checkRaw(RawObject obj,
+ Object[] fixedFields,
+ Object... otherFields) {
+ Object[] allFields =
+ new Object[otherFields.length + fixedFields.length];
+ System.arraycopy(otherFields, 0, allFields, 0, otherFields.length);
+ System.arraycopy(fixedFields, 0, allFields,
+ otherFields.length, fixedFields.length);
+ checkRawFields(obj, allFields);
+ }
+ }
+
+ static class ProxiedClass {
+ int data;
+
+ ProxiedClass(int data) {
+ this.data = data;
+ }
+ }
+
+ @Persistent(version=1, proxyFor=ProxiedClass.class)
+ static class ProxiedClass_Proxy implements PersistentProxy<ProxiedClass> {
+ long data;
+
+ public void initializeProxy(ProxiedClass o) {
+ data = o.data;
+ }
+
+ public ProxiedClass convertProxy() {
+ return new ProxiedClass((int) data);
+ }
+ }
+
+ @Entity
+ static class ProxiedClass_Entity
+ extends EvolveCase {
+
+ private static final String NAME =
+ ProxiedClass_Entity.class.getName();
+ private static final String NAME2 =
+ ProxiedClass_Proxy.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ ProxiedClass embed;
+
+ @Override
+ void configure(EntityModel model, StoreConfig config) {
+ model.registerClass(ProxiedClass_Proxy.class);
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME2, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ProxiedClass_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ProxiedClass_Entity.class);
+ ProxiedClass_Entity obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertNotNull(obj.embed);
+ TestCase.assertEquals(88, obj.embed.data);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ProxiedClass_Entity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ProxiedClass_Entity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ProxiedClass_Entity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawType embedType = store.getModel().getRawType(NAME2);
+ RawObject embed;
+ if (expectEvolved) {
+ embed = new RawObject
+ (embedType, makeValues("data", 88L), null);
+ } else {
+ embed = new RawObject
+ (embedType, makeValues("data", 88), null);
+ }
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed);
+ }
+ }
+
+ @Persistent(proxyFor=StringBuffer.class)
+ static class DisallowChangeProxyFor_Proxy2
+ implements PersistentProxy<StringBuffer> {
+
+ String data;
+
+ public void initializeProxy(StringBuffer o) {
+ data = o.toString();
+ }
+
+ public StringBuffer convertProxy() {
+ return new StringBuffer(data);
+ }
+ }
+
+ @Persistent(proxyFor=StringBuilder.class)
+ static class DisallowChangeProxyFor_Proxy
+ implements PersistentProxy<StringBuilder> {
+
+ String data;
+
+ public void initializeProxy(StringBuilder o) {
+ data = o.toString();
+ }
+
+ public StringBuilder convertProxy() {
+ return new StringBuilder(data);
+ }
+ }
+
+ @Entity
+ static class DisallowChangeProxyFor
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Error when evolving class: java.lang.StringBuffer version: 0 to class: java.lang.StringBuffer version: 0 Error: The proxy class for this type has been changed from: com.sleepycat.persist.test.EvolveClasses$DisallowChangeProxyFor_Proxy to: com.sleepycat.persist.test.EvolveClasses$DisallowChangeProxyFor_Proxy2";
+ }
+
+ @Override
+ void configure(EntityModel model, StoreConfig config) {
+ model.registerClass(DisallowChangeProxyFor_Proxy.class);
+ model.registerClass(DisallowChangeProxyFor_Proxy2.class);
+ }
+ }
+
+ @Persistent
+ static class DisallowDeleteProxyFor_Proxy {
+ String data;
+ }
+
+ @Entity
+ static class DisallowDeleteProxyFor
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Mutation is missing to evolve class: java.lang.StringBuffer version: 0 Error: java.lang.IllegalArgumentException: Class could not be loaded or is not persistent: java.lang.StringBuffer";
+ }
+ }
+
+ @Persistent(version=1)
+ static class ArrayNameChange_Component_Renamed {
+
+ long data;
+ }
+
+ @Entity
+ static class ArrayNameChange_Entity
+ extends EvolveCase {
+
+ private static final String NAME =
+ ArrayNameChange_Entity.class.getName();
+ private static final String NAME2 =
+ ArrayNameChange_Component_Renamed.class.getName();
+ private static final String NAME3 =
+ PREFIX + "ArrayNameChange_Component";
+
+ @PrimaryKey
+ int key;
+
+ ArrayNameChange_Component_Renamed[] embed;
+ ArrayNameChange_Component_Renamed embed2;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addRenamer(new Renamer(NAME3, 0, NAME2));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 1, NAME3, 0);
+ } else {
+ checkVersions(model, NAME2, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ArrayNameChange_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ ArrayNameChange_Entity.class);
+ ArrayNameChange_Entity obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertNotNull(obj.embed);
+ TestCase.assertEquals(1, obj.embed.length);
+ TestCase.assertEquals(88L, obj.embed[0].data);
+ TestCase.assertSame(obj.embed2, obj.embed[0]);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ArrayNameChange_Entity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ ArrayNameChange_Entity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((ArrayNameChange_Entity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ String compTypeName = expectEvolved ? NAME2 : NAME3;
+ String arrayTypeName = "[L" + compTypeName + ';';
+ RawType compType = store.getModel().getRawType(compTypeName);
+ RawType arrayType = store.getModel().getRawType(arrayTypeName);
+ RawObject embed2;
+ if (expectEvolved) {
+ embed2 = new RawObject
+ (compType, makeValues("data", 88L), null);
+ } else {
+ embed2 = new RawObject
+ (compType, makeValues("data", 88), null);
+ }
+ RawObject embed = new RawObject
+ (arrayType, new Object[] { embed2 });
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ checkRawFields(obj, "key", 99, "embed", embed, "embed2", embed2);
+ }
+ }
+
+ enum AddEnumConstant_Enum {
+ A, B, C;
+ }
+
+ @Entity(version=1)
+ static class AddEnumConstant_Entity
+ extends EvolveCase {
+
+ private static final String NAME =
+ AddEnumConstant_Entity.class.getName();
+ private static final String NAME2 =
+ AddEnumConstant_Enum.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ AddEnumConstant_Enum e1;
+ AddEnumConstant_Enum e2;
+ AddEnumConstant_Enum e3 = AddEnumConstant_Enum.C;
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 0, NAME2, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ checkVersions(model, NAME2, 0);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AddEnumConstant_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ AddEnumConstant_Entity.class);
+ AddEnumConstant_Entity obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertSame(AddEnumConstant_Enum.A, obj.e1);
+ TestCase.assertSame(AddEnumConstant_Enum.B, obj.e2);
+ TestCase.assertSame(AddEnumConstant_Enum.C, obj.e3);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AddEnumConstant_Entity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ AddEnumConstant_Entity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((AddEnumConstant_Entity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ RawType enumType = store.getModel().getRawType(NAME2);
+ if (expectUpdated) {
+ checkRawFields(obj, "key", 99,
+ "e1", new RawObject(enumType, "A"),
+ "e2", new RawObject(enumType, "B"),
+ "e3", new RawObject(enumType, "C"));
+ } else {
+ checkRawFields(obj, "key", 99,
+ "e1", new RawObject(enumType, "A"),
+ "e2", new RawObject(enumType, "B"));
+ }
+ }
+ }
+
+ enum InsertEnumConstant_Enum {
+ X, A, Y, B, Z;
+ }
+
+ @Persistent
+ static class InsertEnumConstant_KeyClass
+ implements Comparable<InsertEnumConstant_KeyClass > {
+
+ @KeyField(1)
+ InsertEnumConstant_Enum key;
+
+ private InsertEnumConstant_KeyClass() {}
+
+ InsertEnumConstant_KeyClass(InsertEnumConstant_Enum key) {
+ this.key = key;
+ }
+
+ public int compareTo(InsertEnumConstant_KeyClass o) {
+ /* Use the natural order, in spite of insertions. */
+ return key.compareTo(o.key);
+ }
+ }
+
+ @Entity(version=1)
+ static class InsertEnumConstant_Entity
+ extends EvolveCase {
+
+ private static final String NAME =
+ InsertEnumConstant_Entity.class.getName();
+ private static final String NAME2 =
+ InsertEnumConstant_Enum.class.getName();
+ private static final String NAME3 =
+ InsertEnumConstant_KeyClass.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ InsertEnumConstant_KeyClass secKey;
+
+ InsertEnumConstant_Enum e1;
+ InsertEnumConstant_Enum e2;
+ InsertEnumConstant_Enum e3 = InsertEnumConstant_Enum.X;
+ InsertEnumConstant_Enum e4 = InsertEnumConstant_Enum.Y;
+ InsertEnumConstant_Enum e5 = InsertEnumConstant_Enum.Z;
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 0, NAME2, 0);
+ checkVersions(model, NAME3, 0, NAME3, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ checkVersions(model, NAME2, 0);
+ checkVersions(model, NAME3, 0);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertEnumConstant_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ InsertEnumConstant_Entity.class);
+ InsertEnumConstant_Entity obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ if (updated) {
+ TestCase.assertSame(InsertEnumConstant_Enum.X, obj.secKey.key);
+ } else {
+ TestCase.assertSame(InsertEnumConstant_Enum.A, obj.secKey.key);
+ }
+ TestCase.assertSame(InsertEnumConstant_Enum.A, obj.e1);
+ TestCase.assertSame(InsertEnumConstant_Enum.B, obj.e2);
+ TestCase.assertSame(InsertEnumConstant_Enum.X, obj.e3);
+ TestCase.assertSame(InsertEnumConstant_Enum.Y, obj.e4);
+ TestCase.assertSame(InsertEnumConstant_Enum.Z, obj.e5);
+
+ if (doUpdate) {
+ obj.secKey =
+ new InsertEnumConstant_KeyClass(InsertEnumConstant_Enum.X);
+ index.put(obj);
+ updated = true;
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertEnumConstant_Entity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ InsertEnumConstant_Entity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((InsertEnumConstant_Entity)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ RawType enumType = store.getModel().getRawType(NAME2);
+
+ Map<String, Object> secKeyFields = new HashMap<String, Object>();
+ RawType secKeyType = store.getModel().getRawType(NAME3);
+ RawObject secKeyObject =
+ new RawObject(secKeyType, secKeyFields, null /*superObject*/);
+
+ if (expectUpdated) {
+ secKeyFields.put("key", new RawObject(enumType, "X"));
+ checkRawFields(obj, "key", 99,
+ "secKey", secKeyObject,
+ "e1", new RawObject(enumType, "A"),
+ "e2", new RawObject(enumType, "B"),
+ "e3", new RawObject(enumType, "X"),
+ "e4", new RawObject(enumType, "Y"),
+ "e5", new RawObject(enumType, "Z"));
+ } else {
+ secKeyFields.put("key", new RawObject(enumType, "A"));
+ checkRawFields(obj, "key", 99,
+ "secKey", secKeyObject,
+ "e1", new RawObject(enumType, "A"),
+ "e2", new RawObject(enumType, "B"));
+ }
+ }
+ }
+
+ enum DeleteEnumConstant_Enum {
+ A, C;
+ }
+
+ /**
+ * Don't allow deleting (or renaming, which appears as a deletion) enum
+ * values without mutations.
+ */
+ @Entity
+ static class DeleteEnumConstant_NoMutation
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ DeleteEnumConstant_Enum e1;
+ DeleteEnumConstant_Enum e2;
+ DeleteEnumConstant_Enum e3;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Incompatible enum type changed detected when evolving class: com.sleepycat.persist.test.EvolveClasses$DeleteEnumConstant_Enum version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DeleteEnumConstant_Enum version: 0 Error: Enum values may not be removed: [B]";
+ }
+ }
+
+ /**
+ * With a Deleter, deleted enum values are null. Note that version is not
+ * bumped.
+ */
+ /* Disabled until support for enum deletion is added.
+ @Entity
+ static class DeleteEnumConstant_WithDeleter
+ extends EvolveCase {
+
+ private static final String NAME =
+ DeleteEnumConstant_WithDeleter.class.getName();
+ private static final String NAME2 =
+ DeleteEnumConstant_Enum.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ DeleteEnumConstant_Enum e1;
+ DeleteEnumConstant_Enum e2;
+ DeleteEnumConstant_Enum e3;
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 0, null);
+ checkVersions(model, NAME, 0);
+ if (oldTypesExist) {
+ checkVersions(model, NAME2, 0, NAME2, 0);
+ } else {
+ checkVersions(model, NAME2, 0);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteEnumConstant_WithDeleter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteEnumConstant_WithDeleter.class);
+ DeleteEnumConstant_WithDeleter obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertSame(DeleteEnumConstant_Enum.A, obj.e1);
+ TestCase.assertSame(null, obj.e2);
+ TestCase.assertSame(DeleteEnumConstant_Enum.C, obj.e3);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteEnumConstant_WithDeleter>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ DeleteEnumConstant_WithDeleter.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((DeleteEnumConstant_WithDeleter)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw(store, 99, NAME, 0, CASECLS, 0);
+ RawType enumType = store.getModel().getRawType(NAME2);
+ if (expectUpdated) {
+ checkRawFields(obj, "key", 99,
+ "e1", new RawObject(enumType, "A"),
+ "e2", null,
+ "e3", new RawObject(enumType, "C"));
+ } else {
+ checkRawFields(obj, "key", 99,
+ "e1", new RawObject(enumType, "A"),
+ "e2", new RawObject(enumType, "B"),
+ "e3", new RawObject(enumType, "C"));
+ }
+ }
+ }
+ */
+
+ /**
+ * A field converter can assign deleted enum values. Version must be
+ * bumped when a converter is added.
+ */
+ /* Disabled until support for enum deletion is added.
+ @Entity(version=1)
+ static class DeleteEnumConstant_WithConverter
+ extends EvolveCase {
+
+ private static final String NAME =
+ DeleteEnumConstant_WithConverter.class.getName();
+ private static final String NAME2 =
+ DeleteEnumConstant_Enum.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ DeleteEnumConstant_Enum e1;
+ DeleteEnumConstant_Enum e2;
+ DeleteEnumConstant_Enum e3;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ Conversion c = new MyConversion();
+ m.addConverter(new Converter(NAME, 0, "e1", c));
+ m.addConverter(new Converter(NAME, 0, "e2", c));
+ m.addConverter(new Converter(NAME, 0, "e3", c));
+ return m;
+ }
+
+ @SuppressWarnings("serial")
+ static class MyConversion implements Conversion {
+
+ transient RawType newType;
+
+ public void initialize(EntityModel model) {
+ newType = model.getRawType(NAME2);
+ TestCase.assertNotNull(newType);
+ }
+
+ public Object convert(Object fromValue) {
+ TestCase.assertNotNull(newType);
+ RawObject obj = (RawObject) fromValue;
+ String val = obj.getEnum();
+ TestCase.assertNotNull(val);
+ if ("B".equals(val)) {
+ val = "C";
+ }
+ return new RawObject(newType, val);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof MyConversion;
+ }
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 0, NAME2, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ checkVersions(model, NAME2, 0);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteEnumConstant_WithConverter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteEnumConstant_WithConverter.class);
+ DeleteEnumConstant_WithConverter obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertSame(DeleteEnumConstant_Enum.A, obj.e1);
+ TestCase.assertSame(DeleteEnumConstant_Enum.C, obj.e2);
+ TestCase.assertSame(DeleteEnumConstant_Enum.C, obj.e3);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteEnumConstant_WithConverter>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ DeleteEnumConstant_WithConverter.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((DeleteEnumConstant_WithConverter)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw(store, 99, NAME, expectEvolved ? 1 : 0,
+ CASECLS, 0);
+ RawType enumType = store.getModel().getRawType(NAME2);
+ if (expectEvolved) {
+ checkRawFields(obj, "key", 99,
+ "e1", new RawObject(enumType, "A"),
+ "e2", new RawObject(enumType, "C"),
+ "e3", new RawObject(enumType, "C"));
+ } else {
+ checkRawFields(obj, "key", 99,
+ "e1", new RawObject(enumType, "A"),
+ "e2", new RawObject(enumType, "B"),
+ "e3", new RawObject(enumType, "C"));
+ }
+ }
+ }
+ */
+
+ @Entity
+ static class DisallowChangeKeyRelate
+ extends EvolveCase {
+
+ private static final String NAME =
+ DisallowChangeKeyRelate.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ int skey;
+
+ @Override
+ public String getStoreOpenException() {
+ return "com.sleepycat.persist.evolve.IncompatibleClassException: Change detected in the relate attribute (Relationship) of a secondary key when evolving class: com.sleepycat.persist.test.EvolveClasses$DisallowChangeKeyRelate version: 0 to class: com.sleepycat.persist.test.EvolveClasses$DisallowChangeKeyRelate version: 0 Error: Old key: skey relate: ONE_TO_ONE new key: skey relate: MANY_TO_ONE";
+ }
+ }
+
+ @Entity(version=1)
+ static class AllowChangeKeyMetadata
+ extends EvolveCase {
+
+ private static final String NAME =
+ AllowChangeKeyMetadata.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ /*
+ * Combined fields from version 0 and 1:
+ * addAnnotation = 88;
+ * dropField = 77;
+ * dropAnnotation = 66;
+ * addField = 55;
+ * renamedField = 44; // was toBeRenamedField
+ * aa = 33;
+ * ff = 22;
+ */
+
+ int aa;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int addAnnotation;
+
+ int dropAnnotation;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Integer addField;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int renamedField;
+
+ int ff;
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0, "dropField"));
+ m.addRenamer(new Renamer(NAME, 0, "toBeRenamedField",
+ "renamedField"));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowChangeKeyMetadata>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ AllowChangeKeyMetadata.class);
+ AllowChangeKeyMetadata obj = index.get(99);
+ checkValues(obj);
+
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "addAnnotation").get(88));
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "renamedField").get(44));
+ if (updated) {
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "addField").get(55));
+ } else {
+ TestCase.assertNull(store.getSecondaryIndex
+ (index, Integer.class, "addField").get(55));
+ }
+
+ if (doUpdate) {
+ obj.addField = 55;
+ index.put(obj);
+ updated = true;
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "addAnnotation").get(88));
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "addField").get(55));
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowChangeKeyMetadata>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ AllowChangeKeyMetadata.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((AllowChangeKeyMetadata)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ private void checkValues(AllowChangeKeyMetadata obj) {
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.addAnnotation);
+ TestCase.assertEquals(66, obj.dropAnnotation);
+ TestCase.assertEquals(44, obj.renamedField);
+ TestCase.assertEquals(33, obj.aa);
+ TestCase.assertEquals(22, obj.ff);
+ if (updated) {
+ TestCase.assertEquals(Integer.valueOf(55), obj.addField);
+ } else {
+ TestCase.assertNull(obj.addField);
+ }
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ if (expectUpdated) {
+ checkRawFields(obj, "key", 99,
+ "addAnnotation", 88,
+ "dropAnnotation", 66,
+ "addField", 55,
+ "renamedField", 44,
+ "aa", 33,
+ "ff", 22);
+ } else if (expectEvolved) {
+ checkRawFields(obj, "key", 99,
+ "addAnnotation", 88,
+ "dropAnnotation", 66,
+ "renamedField", 44,
+ "aa", 33,
+ "ff", 22);
+ } else {
+ checkRawFields(obj, "key", 99,
+ "addAnnotation", 88,
+ "dropField", 77,
+ "dropAnnotation", 66,
+ "toBeRenamedField", 44,
+ "aa", 33,
+ "ff", 22);
+ }
+ Environment env = store.getEnvironment();
+ assertDbExists(expectEvolved, env, NAME, "addAnnotation");
+ assertDbExists(expectEvolved, env, NAME, "addField");
+ assertDbExists(expectEvolved, env, NAME, "renamedField");
+ assertDbExists(!expectEvolved, env, NAME, "toBeRenamedField");
+ assertDbExists(!expectEvolved, env, NAME, "dropField");
+ assertDbExists(!expectEvolved, env, NAME, "dropAnnotation");
+ }
+ }
+
+ /**
+ * Same test as AllowChangeKeyMetadata but with the secondary keys in an
+ * entity subclass. [#16253]
+ */
+ @Persistent(version=1)
+ static class AllowChangeKeyMetadataInSubclass
+ extends AllowChangeKeyMetadataEntity {
+
+ private static final String NAME =
+ AllowChangeKeyMetadataInSubclass.class.getName();
+ private static final String NAME2 =
+ AllowChangeKeyMetadataEntity.class.getName();
+
+ /*
+ * Combined fields from version 0 and 1:
+ * addAnnotation = 88;
+ * dropField = 77;
+ * dropAnnotation = 66;
+ * addField = 55;
+ * renamedField = 44; // was toBeRenamedField
+ * aa = 33;
+ * ff = 22;
+ */
+
+ int aa;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int addAnnotation;
+
+ int dropAnnotation;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Integer addField;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int renamedField;
+
+ int ff;
+
+ @Override
+ void configure(EntityModel model, StoreConfig config) {
+ model.registerClass(AllowChangeKeyMetadataInSubclass.class);
+ }
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addDeleter(new Deleter(NAME, 0, "dropField"));
+ m.addRenamer(new Renamer(NAME, 0, "toBeRenamedField",
+ "renamedField"));
+ return m;
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkNonEntity(true, model, env, NAME, 1);
+ checkEntity(true, model, env, NAME2, 0, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ checkVersions(model, NAME2, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ checkVersions(model, NAME2, 0);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowChangeKeyMetadataEntity>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ AllowChangeKeyMetadataEntity.class);
+ AllowChangeKeyMetadataEntity obj = index.get(99);
+ checkValues(obj);
+
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "addAnnotation").get(88));
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "renamedField").get(44));
+ if (updated) {
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "addField").get(55));
+ } else {
+ TestCase.assertNull(store.getSecondaryIndex
+ (index, Integer.class, "addField").get(55));
+ }
+
+ if (doUpdate) {
+ ((AllowChangeKeyMetadataInSubclass) obj).addField = 55;
+ index.put(obj);
+ updated = true;
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "addAnnotation").get(88));
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "addField").get(55));
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowChangeKeyMetadataEntity>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ AllowChangeKeyMetadataEntity.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME2).get(99);
+ index.put((AllowChangeKeyMetadataInSubclass)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ private void checkValues(AllowChangeKeyMetadataEntity objParam) {
+ AllowChangeKeyMetadataInSubclass obj =
+ (AllowChangeKeyMetadataInSubclass) objParam;
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals(88, obj.addAnnotation);
+ TestCase.assertEquals(66, obj.dropAnnotation);
+ TestCase.assertEquals(44, obj.renamedField);
+ TestCase.assertEquals(33, obj.aa);
+ TestCase.assertEquals(22, obj.ff);
+ if (updated) {
+ TestCase.assertEquals(Integer.valueOf(55), obj.addField);
+ } else {
+ TestCase.assertNull(obj.addField);
+ }
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw
+ (store, NAME2, 99, NAME, expectEvolved ? 1 : 0,
+ NAME2, 0, CASECLS, 0);
+ checkRawFields(obj.getSuper(), "key", 99);
+ if (expectUpdated) {
+ checkRawFields(obj,
+ "addAnnotation", 88,
+ "dropAnnotation", 66,
+ "addField", 55,
+ "renamedField", 44,
+ "aa", 33,
+ "ff", 22);
+ } else if (expectEvolved) {
+ checkRawFields(obj,
+ "addAnnotation", 88,
+ "dropAnnotation", 66,
+ "renamedField", 44,
+ "aa", 33,
+ "ff", 22);
+ } else {
+ checkRawFields(obj,
+ "addAnnotation", 88,
+ "dropField", 77,
+ "dropAnnotation", 66,
+ "toBeRenamedField", 44,
+ "aa", 33,
+ "ff", 22);
+ }
+ Environment env = store.getEnvironment();
+ assertDbExists(expectEvolved, env, NAME2, "addAnnotation");
+ assertDbExists(expectEvolved, env, NAME2, "addField");
+ assertDbExists(expectEvolved, env, NAME2, "renamedField");
+ assertDbExists(!expectEvolved, env, NAME2, "toBeRenamedField");
+ assertDbExists(!expectEvolved, env, NAME2, "dropField");
+ assertDbExists(!expectEvolved, env, NAME2, "dropAnnotation");
+ }
+ }
+
+ @Entity
+ static class AllowChangeKeyMetadataEntity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+ }
+
+ /**
+ * Special case of adding secondaries that caused
+ * IndexOutOfBoundsException. [#15524]
+ */
+ @Entity(version=1)
+ static class AllowAddSecondary
+ extends EvolveCase {
+
+ private static final String NAME =
+ AllowAddSecondary.class.getName();
+
+ @PrimaryKey
+ long key;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int a;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int b;
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Long,AllowAddSecondary>
+ index = store.getPrimaryIndex
+ (Long.class,
+ AllowAddSecondary.class);
+ AllowAddSecondary obj = index.get(99L);
+ checkValues(obj);
+
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "a").get(1));
+ if (updated) {
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "b").get(3));
+ TestCase.assertNull(store.getSecondaryIndex
+ (index, Integer.class, "b").get(2));
+ } else {
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "b").get(2));
+ TestCase.assertNull(store.getSecondaryIndex
+ (index, Integer.class, "b").get(3));
+ }
+
+ if (doUpdate) {
+ obj.b = 3;
+ index.put(obj);
+ updated = true;
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "a").get(1));
+ checkValues(store.getSecondaryIndex
+ (index, Integer.class, "b").get(3));
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Long,AllowAddSecondary>
+ index = newStore.getPrimaryIndex
+ (Long.class,
+ AllowAddSecondary.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99L);
+ index.put((AllowAddSecondary)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ private void checkValues(AllowAddSecondary obj) {
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99L, obj.key);
+ TestCase.assertEquals(1, obj.a);
+ if (updated) {
+ TestCase.assertEquals(3, obj.b);
+ } else {
+ TestCase.assertEquals(2, obj.b);
+ }
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw
+ (store, 99L, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ if (expectUpdated) {
+ checkRawFields(obj, "key", 99L,
+ "a", 1,
+ "b", 3);
+ } else {
+ checkRawFields(obj, "key", 99L,
+ "a", 1,
+ "b", 2);
+ }
+ Environment env = store.getEnvironment();
+ assertDbExists(expectEvolved, env, NAME, "a");
+ assertDbExists(expectEvolved, env, NAME, "b");
+ }
+ }
+
+ @Entity(version=1)
+ static class FieldAddAndConvert
+ extends EvolveCase {
+
+ private static final String NAME =
+ FieldAddAndConvert.class.getName();
+
+ @PrimaryKey
+ int key;
+
+ private final String f0 = "0"; // new field
+ private final String f1 = "1"; // converted field
+ private final String f2 = "2"; // new field
+ private final String f3 = "3"; // converted field
+ private final String f4 = "4"; // new field
+
+ @Override
+ Mutations getMutations() {
+ Mutations m = new Mutations();
+ m.addConverter(new Converter(NAME, 0, "f1", new IntToString()));
+ m.addConverter(new Converter(NAME, 0, "f3", new IntToString()));
+ return m;
+ }
+
+ @SuppressWarnings("serial")
+ private static class IntToString implements Conversion {
+
+ public void initialize(EntityModel model) {
+ }
+
+ public Object convert(Object fromValue) {
+ return fromValue.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof IntToString;
+ }
+ }
+
+ @Override
+ void checkEvolvedModel(EntityModel model,
+ Environment env,
+ boolean oldTypesExist) {
+ checkEntity(true, model, env, NAME, 1, null);
+ if (oldTypesExist) {
+ checkVersions(model, NAME, 1, NAME, 0);
+ } else {
+ checkVersions(model, NAME, 1);
+ }
+ }
+
+ @Override
+ void readObjects(EntityStore store, boolean doUpdate)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,FieldAddAndConvert>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ FieldAddAndConvert.class);
+ FieldAddAndConvert obj = index.get(99);
+ TestCase.assertNotNull(obj);
+ TestCase.assertEquals(99, obj.key);
+ TestCase.assertEquals("0", obj.f0);
+ TestCase.assertEquals("1", obj.f1);
+ TestCase.assertEquals("2", obj.f2);
+ TestCase.assertEquals("3", obj.f3);
+ TestCase.assertEquals("4", obj.f4);
+
+ if (doUpdate) {
+ index.put(obj);
+ }
+ }
+
+ @Override
+ void copyRawObjects(RawStore rawStore, EntityStore newStore)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,FieldAddAndConvert>
+ index = newStore.getPrimaryIndex
+ (Integer.class,
+ FieldAddAndConvert.class);
+ RawObject raw = rawStore.getPrimaryIndex(NAME).get(99);
+ index.put((FieldAddAndConvert)
+ newStore.getModel().convertRawObject(raw));
+ }
+
+ @Override
+ void readRawObjects(RawStore store,
+ boolean expectEvolved,
+ boolean expectUpdated)
+ throws DatabaseException {
+
+ RawObject obj = readRaw
+ (store, 99, NAME, expectEvolved ? 1 : 0, CASECLS, 0);
+ if (expectUpdated) {
+ checkRawFields(obj,
+ "key", 99,
+ "f0", "0",
+ "f1", "1",
+ "f2", "2",
+ "f3", "3",
+ "f4", "4");
+ } else if (expectEvolved) {
+ checkRawFields(obj,
+ "key", 99,
+ "f1", "1",
+ "f3", "3");
+ } else {
+ checkRawFields(obj,
+ "key", 99,
+ "f1", 1,
+ "f3", 3);
+ }
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/EvolveClasses.java.original b/test/scr024/src/com/sleepycat/persist/test/EvolveClasses.java.original
new file mode 100644
index 00000000..c077920f
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/EvolveClasses.java.original
@@ -0,0 +1,2855 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.evolve.Mutations;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.EntityModel;
+import com.sleepycat.persist.model.KeyField;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PersistentProxy;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.persist.raw.RawStore;
+
+import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
+
+/**
+ * Nested classes are original versions of classes of the same name in
+ * EvolveClasses.java. See EvolveTestBase.java for the steps that are taken to
+ * add a new class (test case).
+ *
+ * @author Mark Hayes
+ */
+class EvolveClasses {
+
+ @Entity
+ static class DeletedEntity1_ClassRemoved extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedEntity1_ClassRemoved> index =
+ store.getPrimaryIndex
+ (Integer.class, DeletedEntity1_ClassRemoved.class);
+ index.put(this);
+
+ SecondaryIndex<Integer,Integer,DeletedEntity1_ClassRemoved>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ TestCase.assertNotNull(sindex.get(88));
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ assertDbExists
+ (true, env,
+ DeletedEntity1_ClassRemoved.class.getName());
+ assertDbExists
+ (true, env,
+ DeletedEntity1_ClassRemoved.class.getName(), "skey");
+ }
+ }
+
+ @Entity
+ static class DeletedEntity2_ClassRemoved extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedEntity2_ClassRemoved> index =
+ store.getPrimaryIndex
+ (Integer.class, DeletedEntity2_ClassRemoved.class);
+ index.put(this);
+
+ SecondaryIndex<Integer,Integer,DeletedEntity2_ClassRemoved>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ TestCase.assertNotNull(sindex.get(88));
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ assertDbExists
+ (true, env,
+ DeletedEntity2_ClassRemoved.class.getName());
+ assertDbExists
+ (true, env,
+ DeletedEntity2_ClassRemoved.class.getName(), "skey");
+ }
+ }
+
+ @Entity
+ static class DeletedEntity3_AnnotRemoved_NoMutation extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedEntity3_AnnotRemoved_NoMutation>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedEntity3_AnnotRemoved_NoMutation.class);
+ index.put(this);
+
+ SecondaryIndex<Integer,Integer,
+ DeletedEntity3_AnnotRemoved_NoMutation>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ TestCase.assertNotNull(sindex.get(88));
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ assertDbExists
+ (true, env,
+ DeletedEntity3_AnnotRemoved_NoMutation.class.getName());
+ assertDbExists
+ (true, env,
+ DeletedEntity3_AnnotRemoved_NoMutation.class.getName(),
+ "skey");
+ }
+ }
+
+ @Entity
+ static class DeletedEntity4_AnnotRemoved_WithDeleter extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedEntity4_AnnotRemoved_WithDeleter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedEntity4_AnnotRemoved_WithDeleter.class);
+ index.put(this);
+
+ SecondaryIndex<Integer,Integer,
+ DeletedEntity4_AnnotRemoved_WithDeleter>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ TestCase.assertNotNull(sindex.get(88));
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ assertDbExists
+ (true, env,
+ DeletedEntity4_AnnotRemoved_WithDeleter.class.getName());
+ assertDbExists
+ (true, env,
+ DeletedEntity4_AnnotRemoved_WithDeleter.class.getName(),
+ "skey");
+ }
+ }
+
+ @Entity
+ static class DeletedEntity5_EntityToPersist_NoMutation extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedEntity5_EntityToPersist_NoMutation>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedEntity5_EntityToPersist_NoMutation.class);
+ index.put(this);
+
+ SecondaryIndex<Integer,Integer,
+ DeletedEntity5_EntityToPersist_NoMutation>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ TestCase.assertNotNull(sindex.get(88));
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ assertDbExists
+ (true, env,
+ DeletedEntity5_EntityToPersist_NoMutation.class.getName());
+ assertDbExists
+ (true, env,
+ DeletedEntity5_EntityToPersist_NoMutation.class.getName(),
+ "skey");
+ }
+ }
+
+ @Entity
+ static class DeletedEntity6_EntityToPersist_WithDeleter extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedEntity6_EntityToPersist_WithDeleter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedEntity6_EntityToPersist_WithDeleter.class);
+ index.put(this);
+
+ SecondaryIndex<Integer,Integer,
+ DeletedEntity6_EntityToPersist_WithDeleter>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ TestCase.assertNotNull(sindex.get(88));
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ assertDbExists
+ (true, env,
+ DeletedEntity6_EntityToPersist_WithDeleter.class.getName());
+ assertDbExists
+ (true, env,
+ DeletedEntity6_EntityToPersist_WithDeleter.class.getName(),
+ "skey");
+ }
+ }
+
+ @Persistent
+ static class DeletedPersist1_ClassRemoved {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class DeletedPersist1_ClassRemoved_NoMutation extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ DeletedPersist1_ClassRemoved embed =
+ new DeletedPersist1_ClassRemoved();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedPersist1_ClassRemoved_NoMutation>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist1_ClassRemoved_NoMutation.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeletedPersist2_ClassRemoved {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class DeletedPersist2_ClassRemoved_WithDeleter extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ DeletedPersist2_ClassRemoved embed =
+ new DeletedPersist2_ClassRemoved();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedPersist2_ClassRemoved_WithDeleter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist2_ClassRemoved_WithDeleter.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeletedPersist3_AnnotRemoved {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class DeletedPersist3_AnnotRemoved_NoMutation extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ DeletedPersist3_AnnotRemoved embed =
+ new DeletedPersist3_AnnotRemoved();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedPersist3_AnnotRemoved_NoMutation>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist3_AnnotRemoved_NoMutation.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeletedPersist4_AnnotRemoved {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class DeletedPersist4_AnnotRemoved_WithDeleter extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ DeletedPersist4_AnnotRemoved embed =
+ new DeletedPersist4_AnnotRemoved();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedPersist4_AnnotRemoved_WithDeleter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist4_AnnotRemoved_WithDeleter.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeletedPersist5_PersistToEntity {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class DeletedPersist5_PersistToEntity_NoMutation
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ DeletedPersist5_PersistToEntity embed =
+ new DeletedPersist5_PersistToEntity();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedPersist5_PersistToEntity_NoMutation>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist5_PersistToEntity_NoMutation.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeletedPersist6_PersistToEntity {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class DeletedPersist6_PersistToEntity_WithDeleter
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ DeletedPersist6_PersistToEntity embed =
+ new DeletedPersist6_PersistToEntity();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeletedPersist6_PersistToEntity_WithDeleter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeletedPersist6_PersistToEntity_WithDeleter.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class RenamedEntity1_NewEntityName
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,RenamedEntity1_NewEntityName>
+ index = store.getPrimaryIndex
+ (Integer.class, RenamedEntity1_NewEntityName.class);
+ index.put(this);
+
+ SecondaryIndex<Integer,Integer,RenamedEntity1_NewEntityName>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ TestCase.assertNotNull(sindex.get(88));
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ assertDbExists
+ (true, env,
+ RenamedEntity1_NewEntityName.class.getName());
+ assertDbExists
+ (true, env,
+ RenamedEntity1_NewEntityName.class.getName(), "skey");
+ }
+ }
+
+ @Entity
+ static class RenamedEntity2_NewEntityName
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,RenamedEntity2_NewEntityName>
+ index = store.getPrimaryIndex
+ (Integer.class, RenamedEntity2_NewEntityName.class);
+ index.put(this);
+
+ SecondaryIndex<Integer,Integer,RenamedEntity2_NewEntityName>
+ sindex = store.getSecondaryIndex(index, Integer.class, "skey");
+ TestCase.assertNotNull(sindex.get(88));
+ }
+
+ @Override
+ void checkUnevolvedModel(EntityModel model, Environment env) {
+ assertDbExists
+ (true, env,
+ RenamedEntity2_NewEntityName.class.getName());
+ assertDbExists
+ (true, env,
+ RenamedEntity2_NewEntityName.class.getName(), "skey");
+ }
+ }
+
+ @Persistent
+ static class DeleteSuperclass1_BaseClass
+ extends EvolveCase {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class DeleteSuperclass1_NoMutation
+ extends DeleteSuperclass1_BaseClass {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass1_NoMutation>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass1_NoMutation.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeleteSuperclass2_BaseClass
+ extends EvolveCase {
+
+ int f = 123;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 456;
+ }
+
+ @Entity
+ static class DeleteSuperclass2_WithConverter
+ extends DeleteSuperclass2_BaseClass {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey2 = 77;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Integer skey3 = 66;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass2_WithConverter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass2_WithConverter.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeleteSuperclass3_BaseClass
+ extends EvolveCase {
+
+ int f = 123;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey = 456;
+ }
+
+ @Entity
+ static class DeleteSuperclass3_WithDeleter
+ extends DeleteSuperclass3_BaseClass {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass3_WithDeleter>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass3_WithDeleter.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeleteSuperclass4_BaseClass
+ extends EvolveCase {
+ }
+
+ @Entity
+ static class DeleteSuperclass4_NoFields
+ extends DeleteSuperclass4_BaseClass {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass4_NoFields>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass4_NoFields.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DeleteSuperclass5_Embedded_Base {
+
+ int g = 456;
+ }
+
+ @Persistent
+ static class DeleteSuperclass5_Embedded
+ extends DeleteSuperclass5_Embedded_Base {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class DeleteSuperclass5_Top
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ DeleteSuperclass5_Embedded embed =
+ new DeleteSuperclass5_Embedded();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteSuperclass5_Top>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DeleteSuperclass5_Top.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class InsertSuperclass1_Between
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertSuperclass1_Between>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ InsertSuperclass1_Between.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class InsertSuperclass2_Embedded {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class InsertSuperclass2_Top
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ InsertSuperclass2_Embedded embed =
+ new InsertSuperclass2_Embedded();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertSuperclass2_Top>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ InsertSuperclass2_Top.class);
+ index.put(this);
+ }
+ }
+
+ /*
+ @Persistent
+ static class RenameFields1_Base
+ extends EvolveCase {
+
+ int f = 123;
+ }
+
+ @Entity
+ static class RenameFields1
+ extends RenameFields1_Base {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,RenameFields1>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ RenameFields1.class);
+ index.put(this);
+ }
+ }
+ */
+
+ @Entity
+ static class DisallowNonKeyField_PrimitiveToObject
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_PrimitiveToObject>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_PrimitiveToObject.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_ObjectToPrimitive
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ String ff = "88";
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_ObjectToPrimitive>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_ObjectToPrimitive.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class MyType {
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MyType;
+ }
+ }
+
+ @Persistent
+ static class MySubtype extends MyType {
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MySubtype;
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_ObjectToSubtype
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ MyType ff = new MyType();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_ObjectToSubtype>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_ObjectToSubtype.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_ObjectToUnrelatedSimple
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Integer ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_ObjectToUnrelatedSimple>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_ObjectToUnrelatedSimple.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_ObjectToUnrelatedOther
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Integer ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_ObjectToUnrelatedOther>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_ObjectToUnrelatedOther.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_byte2boolean
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ byte ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_byte2boolean>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_byte2boolean.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_short2byte
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ short ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_short2byte>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_short2byte.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_int2short
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_int2short>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_int2short.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_long2int
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ long ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_long2int>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_long2int.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_float2long
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ float ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_float2long>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_float2long.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_double2float
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ double ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_double2float>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_double2float.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_Byte2byte
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Byte ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_Byte2byte>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_Byte2byte.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_Character2char
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Character ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_Character2char>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_Character2char.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_Short2short
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Short ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_Short2short>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_Short2short.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_Integer2int
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Integer ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_Integer2int>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_Integer2int.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_Long2long
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Long ff = 88L;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_Long2long>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_Long2long.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_Float2float
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Float ff = 88F;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_Float2float>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_Float2float.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_Double2double
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ Double ff = 88D;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_Double2double>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_Double2double.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_float2BigInt
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ float ff = 88F;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_float2BigInt>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_float2BigInt.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowNonKeyField_BigInt2long
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ BigInteger ff = BigInteger.valueOf(88);
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowNonKeyField_BigInt2long>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowNonKeyField_BigInt2long.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_byte2short
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ byte ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_byte2short>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_byte2short.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_char2int
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ char ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_char2int>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_char2int.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_short2int
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ short ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_short2int>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_short2int.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_int2long
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_int2long>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_int2long.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_long2float
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ long ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_long2float>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_long2float.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_float2double
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ float ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_float2double>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_float2double.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_Byte2short2
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Byte ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_Byte2short2>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_Byte2short2.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_Character2int
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Character ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_Character2int>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_Character2int.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_Short2int2
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Short ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_Short2int2>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_Short2int2.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_Integer2long
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Integer ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_Integer2long>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_Integer2long.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_Long2float2
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Long ff = 88L;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_Long2float2>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_Long2float2.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_Float2double2
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ Float ff = 88F;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_Float2double2>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_Float2double2.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowSecKeyField_int2BigInt
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int ff = 88;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowSecKeyField_int2BigInt>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowSecKeyField_int2BigInt.class);
+ index.put(this);
+ }
+ }
+
+ // --
+
+ @Entity
+ static class DisallowPriKeyField_byte2short
+ extends EvolveCase {
+
+ @PrimaryKey
+ byte key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Byte,DisallowPriKeyField_byte2short>
+ index = store.getPrimaryIndex
+ (Byte.class,
+ DisallowPriKeyField_byte2short.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_char2int
+ extends EvolveCase {
+
+ @PrimaryKey
+ char key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Character,DisallowPriKeyField_char2int>
+ index = store.getPrimaryIndex
+ (Character.class,
+ DisallowPriKeyField_char2int.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_short2int
+ extends EvolveCase {
+
+ @PrimaryKey
+ short key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Short,DisallowPriKeyField_short2int>
+ index = store.getPrimaryIndex
+ (Short.class,
+ DisallowPriKeyField_short2int.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_int2long
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowPriKeyField_int2long>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowPriKeyField_int2long.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_long2float
+ extends EvolveCase {
+
+ @PrimaryKey
+ long key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Long,DisallowPriKeyField_long2float>
+ index = store.getPrimaryIndex
+ (Long.class,
+ DisallowPriKeyField_long2float.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_float2double
+ extends EvolveCase {
+
+ @PrimaryKey
+ float key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Float,DisallowPriKeyField_float2double>
+ index = store.getPrimaryIndex
+ (Float.class,
+ DisallowPriKeyField_float2double.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_Byte2short2
+ extends EvolveCase {
+
+ @PrimaryKey
+ Byte key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Byte,DisallowPriKeyField_Byte2short2>
+ index = store.getPrimaryIndex
+ (Byte.class,
+ DisallowPriKeyField_Byte2short2.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_Character2int
+ extends EvolveCase {
+
+ @PrimaryKey
+ Character key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Character,DisallowPriKeyField_Character2int>
+ index = store.getPrimaryIndex
+ (Character.class,
+ DisallowPriKeyField_Character2int.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_Short2int2
+ extends EvolveCase {
+
+ @PrimaryKey
+ Short key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Short,DisallowPriKeyField_Short2int2>
+ index = store.getPrimaryIndex
+ (Short.class,
+ DisallowPriKeyField_Short2int2.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_Integer2long
+ extends EvolveCase {
+
+ @PrimaryKey
+ Integer key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowPriKeyField_Integer2long>
+ index = store.getPrimaryIndex
+ (Integer.class,
+ DisallowPriKeyField_Integer2long.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_Long2float2
+ extends EvolveCase {
+
+ @PrimaryKey
+ Long key = 99L;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Long,DisallowPriKeyField_Long2float2>
+ index = store.getPrimaryIndex
+ (Long.class,
+ DisallowPriKeyField_Long2float2.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_Float2double2
+ extends EvolveCase {
+
+ @PrimaryKey
+ Float key = 99F;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Float,DisallowPriKeyField_Float2double2>
+ index = store.getPrimaryIndex
+ (Float.class,
+ DisallowPriKeyField_Float2double2.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class DisallowPriKeyField_Long2BigInt
+ extends EvolveCase {
+
+ @PrimaryKey
+ Long key = 99L;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Long,DisallowPriKeyField_Long2BigInt>
+ index = store.getPrimaryIndex
+ (Long.class,
+ DisallowPriKeyField_Long2BigInt.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class DisallowCompositeKeyField_byte2short_Key {
+
+ @KeyField(1)
+ int f1 = 1;
+
+ @KeyField(2)
+ byte f2 = 2;
+
+ @KeyField(3)
+ String f3 = "3";
+ }
+
+ @Entity
+ static class DisallowCompositeKeyField_byte2short
+ extends EvolveCase {
+
+ @PrimaryKey
+ DisallowCompositeKeyField_byte2short_Key key =
+ new DisallowCompositeKeyField_byte2short_Key();
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<DisallowCompositeKeyField_byte2short_Key,
+ DisallowCompositeKeyField_byte2short>
+ index = store.getPrimaryIndex
+ (DisallowCompositeKeyField_byte2short_Key.class,
+ DisallowCompositeKeyField_byte2short.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class AllowPriKeyField_byte2Byte
+ extends EvolveCase {
+
+ @PrimaryKey
+ byte key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Byte,AllowPriKeyField_byte2Byte>
+ index = store.getPrimaryIndex
+ (Byte.class, AllowPriKeyField_byte2Byte.class);
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class AllowPriKeyField_Byte2byte2
+ extends EvolveCase {
+
+ @PrimaryKey
+ Byte key = 99;
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Byte,AllowPriKeyField_Byte2byte2>
+ index = store.getPrimaryIndex
+ (Byte.class, AllowPriKeyField_Byte2byte2.class);
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class AllowFieldTypeChanges_Key {
+
+ AllowFieldTypeChanges_Key() {
+ this(false);
+ }
+
+ AllowFieldTypeChanges_Key(boolean init) {
+ if (init) {
+ f1 = true;
+ f2 = (byte) 2;
+ f3 = (short) 3;
+ f4 = 4;
+ f5 = 5L;
+ f6 = 6F;
+ f7 = 7D;
+ f8 = (char) 8;
+ f9 = true;
+ f10 = (byte) 10;
+ f11 = (short) 11;
+ f12 = 12;
+ f13 = 13L;
+ f14 = 14F;
+ f15 = 15D;
+ f16 = (char) 16;
+ }
+ }
+
+ @KeyField(1)
+ boolean f1;
+
+ @KeyField(2)
+ byte f2;
+
+ @KeyField(3)
+ short f3;
+
+ @KeyField(4)
+ int f4;
+
+ @KeyField(5)
+ long f5;
+
+ @KeyField(6)
+ float f6;
+
+ @KeyField(7)
+ double f7;
+
+ @KeyField(8)
+ char f8;
+
+ @KeyField(9)
+ Boolean f9;
+
+ @KeyField(10)
+ Byte f10;
+
+ @KeyField(11)
+ Short f11;
+
+ @KeyField(12)
+ Integer f12;
+
+ @KeyField(13)
+ Long f13;
+
+ @KeyField(14)
+ Float f14;
+
+ @KeyField(15)
+ Double f15;
+
+ @KeyField(16)
+ Character f16;
+ }
+
+ @Persistent
+ static class AllowFieldTypeChanges_Base
+ extends EvolveCase {
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ AllowFieldTypeChanges_Key kcomposite =
+ new AllowFieldTypeChanges_Key(true);
+
+ long f_long2Integer = 111;
+ String f_String2Long = "222";
+ }
+
+ @Entity
+ static class AllowFieldTypeChanges
+ extends AllowFieldTypeChanges_Base {
+
+ @PrimaryKey
+ int pkeyint = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ boolean kboolean = true;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ byte kbyte = 77;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ short kshort = 66;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int kint = 55;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ long klong = 44;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ float kfloat = 33;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ double kdouble = 22;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ char kchar = 11;
+
+ byte f01;
+ byte f02;
+ byte f03;
+ byte f04;
+ byte f06;
+ short f07;
+ short f08;
+ short f09;
+ short f10;
+ char f11;
+ char f12;
+ char f13;
+ char f14;
+ int f15;
+ int f16;
+ int f17;
+ long f18;
+ long f19;
+ float f20;
+
+ byte f21;
+ byte f22;
+ byte f23;
+ byte f24;
+ byte f26;
+ short f27;
+ short f28;
+ short f29;
+ short f30;
+ char f31;
+ char f32;
+ char f33;
+ char f34;
+ int f35;
+ int f36;
+ int f37;
+ long f38;
+ long f39;
+ float f40;
+
+ Byte f41;
+ Byte f42;
+ Byte f43;
+ Byte f44;
+ Byte f46;
+ Short f47;
+ Short f48;
+ Short f49;
+ Short f50;
+ Character f51;
+ Character f52;
+ Character f53;
+ Character f54;
+ Integer f55;
+ Integer f56;
+ Integer f57;
+ Long f58;
+ Long f59;
+ Float f60;
+
+ byte f70;
+ short f71;
+ char f72;
+ int f73;
+ long f74;
+ Byte f75;
+ Short f76;
+ Character f77;
+ Integer f78;
+ Long f79;
+
+ long f_long2int = 333;
+ String f_String2long = "444";
+
+ private void init() {
+ f01 = (byte) 1;
+ f02 = (byte) 2;
+ f03 = (byte) 3;
+ f04 = (byte) 4;
+ f06 = (byte) 6;
+ f07 = (short) 7;
+ f08 = (short) 8;
+ f09 = (short) 9;
+ f10 = (short) 10;
+ f11 = (char) 11;
+ f12 = (char) 12;
+ f13 = (char) 13;
+ f14 = (char) 14;
+ f15 = 15;
+ f16 = 16;
+ f17 = 17;
+ f18 = (long) 18;
+ f19 = (long) 19;
+ f20 = (float) 20;
+
+ f21 = (byte) 21;
+ f22 = (byte) 22;
+ f23 = (byte) 23;
+ f24 = (byte) 24;
+ f26 = (byte) 26;
+ f27 = (short) 27;
+ f28 = (short) 28;
+ f29 = (short) 29;
+ f30 = (short) 30;
+ f31 = (char) 31;
+ f32 = (char) 32;
+ f33 = (char) 33;
+ f34 = (char) 34;
+ f35 = 35;
+ f36 = 36;
+ f37 = 37;
+ f38 = (long) 38;
+ f39 = (long) 39;
+ f40 = (float) 40;
+
+ f41 = (byte) 41;
+ f42 = (byte) 42;
+ f43 = (byte) 43;
+ f44 = (byte) 44;
+ f46 = (byte) 46;
+ f47 = (short) 47;
+ f48 = (short) 48;
+ f49 = (short) 49;
+ f50 = (short) 50;
+ f51 = (char) 51;
+ f52 = (char) 52;
+ f53 = (char) 53;
+ f54 = (char) 54;
+ f55 = 55;
+ f56 = 56;
+ f57 = 57;
+ f58 = (long) 58;
+ f59 = (long) 59;
+ f60 = (float) 60;
+
+ f70 = (byte) 70;
+ f71 = (short) 71;
+ f72 = (char) 72;
+ f73 = 73;
+ f74 = (long) 74;
+ f75 = (byte) 75;
+ f76 = (short) 76;
+ f77 = (char) 77;
+ f78 = 78;
+ f79 = (long) 79;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowFieldTypeChanges>
+ index = store.getPrimaryIndex
+ (Integer.class, AllowFieldTypeChanges.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class ConvertFieldContent_Entity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ String f1;
+ String f2;
+
+ private void init() {
+ key = 99;
+ f1 = "01234";
+ f2 = "56789";
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertFieldContent_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class, ConvertFieldContent_Entity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class ConvertExample1_Address {
+ String street;
+ String city;
+ String state;
+ String zipCode;
+ }
+
+ @Entity
+ static class ConvertExample1_Entity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ ConvertExample1_Address embed;
+
+ private void init() {
+ key = 99;
+ embed = new ConvertExample1_Address();
+ embed.street = "street";
+ embed.city = "city";
+ embed.state = "state";
+ embed.zipCode = "12345";
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample1_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class, ConvertExample1_Entity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class ConvertExample2_Person
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ String address;
+
+ private void init() {
+ key = 99;
+ address = "street#city#state#12345";
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample2_Person>
+ index = store.getPrimaryIndex
+ (Integer.class, ConvertExample2_Person.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class ConvertExample3_Person
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ String street;
+ String city;
+ String state;
+ int zipCode;
+
+ private void init() {
+ key = 99;
+ street = "street";
+ city = "city";
+ state = "state";
+ zipCode = 12345;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample3_Person>
+ index = store.getPrimaryIndex
+ (Integer.class, ConvertExample3_Person.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class ConvertExample3Reverse_Address {
+ String street;
+ String city;
+ String state;
+ int zipCode;
+ }
+
+ @Entity
+ static class ConvertExample3Reverse_Person
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ ConvertExample3Reverse_Address address;
+
+ private void init() {
+ key = 99;
+ address = new ConvertExample3Reverse_Address();
+ address.street = "street";
+ address.city = "city";
+ address.state = "state";
+ address.zipCode = 12345;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample3Reverse_Person>
+ index = store.getPrimaryIndex
+ (Integer.class, ConvertExample3Reverse_Person.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class ConvertExample4_A extends ConvertExample4_B {
+ String name;
+ }
+
+ @Persistent
+ static class ConvertExample4_B {
+ }
+
+ @Entity
+ static class ConvertExample4_Entity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ ConvertExample4_A embed;
+
+ private void init() {
+ key = 99;
+ embed = new ConvertExample4_A();
+ embed.name = "name";
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample4_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class, ConvertExample4_Entity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class ConvertExample5_Pet {
+ String name;
+ boolean isCatNotDog;
+ int finickyLevel;
+ double barkVolume;
+ }
+
+ @Entity
+ static class ConvertExample5_Entity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ ConvertExample5_Pet cat;
+ ConvertExample5_Pet dog;
+
+ private void init() {
+ key = 99;
+ cat = new ConvertExample5_Pet();
+ cat.name = "Jeffry";
+ cat.isCatNotDog = true;
+ cat.finickyLevel = 999;
+ dog = new ConvertExample5_Pet();
+ dog.name = "Nelson";
+ dog.isCatNotDog = false;
+ dog.barkVolume = 0.01;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ConvertExample5_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class, ConvertExample5_Entity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class AllowFieldAddDelete_Embed {
+ private int f1 = 1;
+ private String f2 = "2";
+ private String f4 = "4";
+ private int f6 = 6;
+ private String f7 = "7";
+ }
+
+ @Persistent
+ static class AllowFieldAddDelete_Base
+ extends EvolveCase {
+
+ private int f1 = 1;
+ private String f2 = "2";
+ private String f4 = "4";
+ private int f6 = 6;
+ private String f7 = "7";
+ }
+
+ @Entity
+ static class AllowFieldAddDelete
+ extends AllowFieldAddDelete_Base {
+
+ @PrimaryKey
+ int key;
+
+ AllowFieldAddDelete_Embed embed;
+
+ private int f1 = 1;
+ private String f2 = "2";
+ private String f4 = "4";
+ private int f6 = 6;
+ private String f7 = "7";
+
+ private void init() {
+ key = 99;
+ embed = new AllowFieldAddDelete_Embed();
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowFieldAddDelete>
+ index = store.getPrimaryIndex
+ (Integer.class, AllowFieldAddDelete.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ static class ProxiedClass {
+ int data;
+
+ ProxiedClass(int data) {
+ this.data = data;
+ }
+ }
+
+ @Persistent(proxyFor=ProxiedClass.class)
+ static class ProxiedClass_Proxy implements PersistentProxy<ProxiedClass> {
+ int data;
+
+ public void initializeProxy(ProxiedClass o) {
+ data = o.data;
+ }
+
+ public ProxiedClass convertProxy() {
+ return new ProxiedClass(data);
+ }
+ }
+
+ @Entity
+ static class ProxiedClass_Entity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ ProxiedClass embed;
+
+ private void init() {
+ key = 99;
+ embed = new ProxiedClass(88);
+ }
+
+ @Override
+ void configure(EntityModel model, StoreConfig config) {
+ model.registerClass(ProxiedClass_Proxy.class);
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ProxiedClass_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class, ProxiedClass_Entity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Persistent(proxyFor=StringBuffer.class)
+ static class DisallowChangeProxyFor_Proxy
+ implements PersistentProxy<StringBuffer> {
+
+ String data;
+
+ public void initializeProxy(StringBuffer o) {
+ data = o.toString();
+ }
+
+ public StringBuffer convertProxy() {
+ return new StringBuffer(data);
+ }
+ }
+
+ @Entity
+ static class DisallowChangeProxyFor
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ private void init() {
+ key = 99;
+ }
+
+ @Override
+ void configure(EntityModel model, StoreConfig config) {
+ model.registerClass(DisallowChangeProxyFor_Proxy.class);
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowChangeProxyFor>
+ index = store.getPrimaryIndex
+ (Integer.class, DisallowChangeProxyFor.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Persistent(proxyFor=StringBuffer.class)
+ static class DisallowDeleteProxyFor_Proxy
+ implements PersistentProxy<StringBuffer> {
+
+ String data;
+
+ public void initializeProxy(StringBuffer o) {
+ data = o.toString();
+ }
+
+ public StringBuffer convertProxy() {
+ return new StringBuffer(data);
+ }
+ }
+
+ @Entity
+ static class DisallowDeleteProxyFor
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ private void init() {
+ key = 99;
+ }
+
+ @Override
+ void configure(EntityModel model, StoreConfig config) {
+ model.registerClass(DisallowDeleteProxyFor_Proxy.class);
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowDeleteProxyFor>
+ index = store.getPrimaryIndex
+ (Integer.class, DisallowDeleteProxyFor.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Persistent
+ static class ArrayNameChange_Component {
+
+ int data;
+ }
+
+ @Entity
+ static class ArrayNameChange_Entity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ ArrayNameChange_Component[] embed;
+ ArrayNameChange_Component embed2;
+
+ private void init() {
+ key = 99;
+ embed2 = new ArrayNameChange_Component();
+ embed2.data = 88;
+ embed = new ArrayNameChange_Component[] { embed2 };
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,ArrayNameChange_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class, ArrayNameChange_Entity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ enum AddEnumConstant_Enum {
+ A, B;
+ }
+
+ @Entity
+ static class AddEnumConstant_Entity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ AddEnumConstant_Enum e1;
+ AddEnumConstant_Enum e2;
+
+ private void init() {
+ key = 99;
+ e1 = AddEnumConstant_Enum.A;
+ e2 = AddEnumConstant_Enum.B;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AddEnumConstant_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class, AddEnumConstant_Entity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ enum InsertEnumConstant_Enum {
+ A, B;
+ }
+
+ @Persistent
+ static class InsertEnumConstant_KeyClass
+ implements Comparable<InsertEnumConstant_KeyClass > {
+
+ @KeyField(1)
+ InsertEnumConstant_Enum key;
+
+ private InsertEnumConstant_KeyClass() {}
+
+ InsertEnumConstant_KeyClass(InsertEnumConstant_Enum key) {
+ this.key = key;
+ }
+
+ public int compareTo(InsertEnumConstant_KeyClass o) {
+ /* Use the natural order, in spite of insertions. */
+ return key.compareTo(o.key);
+ }
+ }
+
+ @Entity
+ static class InsertEnumConstant_Entity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ InsertEnumConstant_KeyClass secKey;
+
+ InsertEnumConstant_Enum e1;
+ InsertEnumConstant_Enum e2;
+
+ private void init() {
+ key = 99;
+ secKey =
+ new InsertEnumConstant_KeyClass(InsertEnumConstant_Enum.A);
+ e1 = InsertEnumConstant_Enum.A;
+ e2 = InsertEnumConstant_Enum.B;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,InsertEnumConstant_Entity>
+ index = store.getPrimaryIndex
+ (Integer.class, InsertEnumConstant_Entity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ enum DeleteEnumConstant_Enum {
+ A, B, C;
+ }
+
+ @Entity
+ static class DeleteEnumConstant_NoMutation
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ DeleteEnumConstant_Enum e1;
+ DeleteEnumConstant_Enum e2;
+ DeleteEnumConstant_Enum e3;
+
+ private void init() {
+ key = 99;
+ e1 = DeleteEnumConstant_Enum.A;
+ e2 = DeleteEnumConstant_Enum.B;
+ e3 = DeleteEnumConstant_Enum.C;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteEnumConstant_NoMutation>
+ index = store.getPrimaryIndex
+ (Integer.class, DeleteEnumConstant_NoMutation.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ /* Disabled until support for enum deletion is added.
+ @Entity
+ static class DeleteEnumConstant_WithConverter
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ DeleteEnumConstant_Enum e1;
+ DeleteEnumConstant_Enum e2;
+ DeleteEnumConstant_Enum e3;
+
+ private void init() {
+ key = 99;
+ e1 = DeleteEnumConstant_Enum.A;
+ e2 = DeleteEnumConstant_Enum.B;
+ e3 = DeleteEnumConstant_Enum.C;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DeleteEnumConstant_WithConverter>
+ index = store.getPrimaryIndex
+ (Integer.class, DeleteEnumConstant_WithConverter.class);
+ init();
+ index.put(this);
+ }
+ }
+ */
+
+ @Entity
+ static class DisallowChangeKeyRelate
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int skey;
+
+ private void init() {
+ key = 99;
+ skey = 88;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,DisallowChangeKeyRelate>
+ index = store.getPrimaryIndex
+ (Integer.class, DisallowChangeKeyRelate.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class AllowChangeKeyMetadata
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ int aa;
+
+ int addAnnotation;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int dropField;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int dropAnnotation;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int toBeRenamedField;
+
+ int ff;
+
+ private void init() {
+ key = 99;
+ addAnnotation = 88;
+ dropField = 77;
+ dropAnnotation = 66;
+ toBeRenamedField = 44;
+ aa = 33;
+ ff = 22;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowChangeKeyMetadata>
+ index = store.getPrimaryIndex
+ (Integer.class, AllowChangeKeyMetadata.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ /** [#16253] */
+ @Persistent
+ static class AllowChangeKeyMetadataInSubclass
+ extends AllowChangeKeyMetadataEntity {
+
+ int aa;
+
+ int addAnnotation;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int dropField;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int dropAnnotation;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ int toBeRenamedField;
+
+ int ff;
+
+ private void init() {
+ key = 99;
+ addAnnotation = 88;
+ dropField = 77;
+ dropAnnotation = 66;
+ toBeRenamedField = 44;
+ aa = 33;
+ ff = 22;
+ }
+
+ @Override
+ void configure(EntityModel model, StoreConfig config) {
+ model.registerClass(AllowChangeKeyMetadataInSubclass.class);
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,AllowChangeKeyMetadataEntity>
+ index = store.getPrimaryIndex
+ (Integer.class, AllowChangeKeyMetadataEntity.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ @Entity
+ static class AllowChangeKeyMetadataEntity
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+ }
+
+ /** [#15524] */
+ @Entity
+ static class AllowAddSecondary
+ extends EvolveCase {
+
+ @PrimaryKey
+ long key;
+
+ int a;
+ int b;
+
+ private void init() {
+ key = 99;
+ a = 1;
+ b = 2;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Long,AllowAddSecondary>
+ index = store.getPrimaryIndex
+ (Long.class, AllowAddSecondary.class);
+ init();
+ index.put(this);
+ }
+ }
+
+ /** [#15797] */
+ @Entity
+ static class FieldAddAndConvert
+ extends EvolveCase {
+
+ @PrimaryKey
+ int key;
+
+ private int f1 = 1;
+ private int f3 = 3;
+
+ private void init() {
+ key = 99;
+ }
+
+ @Override
+ void writeObjects(EntityStore store)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,FieldAddAndConvert>
+ index = store.getPrimaryIndex
+ (Integer.class, FieldAddAndConvert.class);
+ init();
+ index.put(this);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/EvolveTest.java b/test/scr024/src/com/sleepycat/persist/test/EvolveTest.java
new file mode 100644
index 00000000..c76a63a7
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/EvolveTest.java
@@ -0,0 +1,255 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.persist.test;
+
+import java.io.IOException;
+
+import junit.framework.Test;
+
+import com.sleepycat.persist.evolve.EvolveConfig;
+import com.sleepycat.persist.evolve.EvolveEvent;
+import com.sleepycat.persist.evolve.EvolveListener;
+import com.sleepycat.persist.evolve.EvolveStats;
+import com.sleepycat.persist.impl.PersistCatalog;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * Runs part two of the EvolveTest. This part is run with the new/updated
+ * version of EvolveClasses in the classpath. It uses the environment and
+ * store created by EvolveTestInit. It verifies that it can read/write/evolve
+ * objects serialized using the old class format, and that it can create new
+ * objects with the new class format.
+ *
+ * @author Mark Hayes
+ */
+public class EvolveTest extends EvolveTestBase {
+
+ /* Toggle to use listener every other test case. */
+ private static boolean useEvolveListener;
+
+ public static Test suite()
+ throws Exception {
+
+ return getSuite(EvolveTest.class);
+ }
+
+ private int evolveNRead;
+ private int evolveNConverted;
+
+ boolean useEvolvedClass() {
+ return true;
+ }
+
+ @Override
+ public void tearDown() {
+ try { super.tearDown(); } catch (Throwable e) { }
+ }
+
+ @Override
+ public void setUp()
+ throws IOException {
+
+ /* Copy the log files created by EvolveTestInit. */
+ envHome = getTestInitHome(true /*evolved*/);
+ envHome.mkdirs();
+ SharedTestUtils.emptyDir(envHome);
+ SharedTestUtils.copyFiles(getTestInitHome(false /*evolved*/), envHome);
+ }
+
+ public void testLazyEvolve()
+ throws Exception {
+
+ openEnv();
+
+ /*
+ * Open in raw mode to check unevolved raw object and formats. This
+ * is possible whether or not we can open the store further below to
+ * evolve formats without errors.
+ */
+ openRawStore();
+ caseObj.checkUnevolvedModel(rawStore.getModel(), env);
+ caseObj.readRawObjects
+ (rawStore, false /*expectEvolved*/, false /*expectUpdated*/);
+ closeRawStore();
+
+ if (openStoreReadWrite()) {
+
+ /*
+ * When opening read-write, formats are evolved lazily. Check by
+ * reading evolved objects.
+ */
+ caseObj.checkEvolvedModel
+ (store.getModel(), env, true /*oldTypesExist*/);
+ caseObj.readObjects(store, false /*doUpdate*/);
+ closeStore();
+
+ /*
+ * Read raw objects again to check that the evolved objects are
+ * returned even though the stored objects were not evolved.
+ */
+ openRawStore();
+ caseObj.checkEvolvedModel
+ (rawStore.getModel(), env, true /*oldTypesExist*/);
+ caseObj.readRawObjects
+ (rawStore, true /*expectEvolved*/, false /*expectUpdated*/);
+ closeRawStore();
+
+ /*
+ * Open read-only to ensure that the catalog does not need to
+ * change (evolve formats) unnecessarily.
+ */
+ PersistCatalog.expectNoClassChanges = true;
+ try {
+ openStoreReadOnly();
+ } finally {
+ PersistCatalog.expectNoClassChanges = false;
+ }
+ caseObj.checkEvolvedModel
+ (store.getModel(), env, true /*oldTypesExist*/);
+ caseObj.readObjects(store, false /*doUpdate*/);
+ closeStore();
+
+ /*
+ * Open read-write to update objects and store them in evolved
+ * format.
+ */
+ openStoreReadWrite();
+ caseObj.checkEvolvedModel
+ (store.getModel(), env, true /*oldTypesExist*/);
+ caseObj.readObjects(store, true /*doUpdate*/);
+ caseObj.checkEvolvedModel
+ (store.getModel(), env, true /*oldTypesExist*/);
+ closeStore();
+
+ /*
+ * Check raw objects again after the evolved objects were stored.
+ */
+ openRawStore();
+ caseObj.checkEvolvedModel
+ (rawStore.getModel(), env, true /*oldTypesExist*/);
+ caseObj.readRawObjects
+ (rawStore, true /*expectEvolved*/, true /*expectUpdated*/);
+ closeRawStore();
+ }
+
+ closeAll();
+ }
+
+ public void testEagerEvolve()
+ throws Exception {
+
+ /* If the store cannot be opened, this test is not appropriate. */
+ if (caseObj.getStoreOpenException() != null) {
+ return;
+ }
+
+ EvolveConfig config = new EvolveConfig();
+
+ /*
+ * Use listener every other time to ensure that the stats are returned
+ * correctly when no listener is configured. [#17024]
+ */
+ useEvolveListener = !useEvolveListener;
+ if (useEvolveListener) {
+ config.setEvolveListener(new EvolveListener() {
+ public boolean evolveProgress(EvolveEvent event) {
+ EvolveStats stats = event.getStats();
+ evolveNRead = stats.getNRead();
+ evolveNConverted = stats.getNConverted();
+ return true;
+ }
+ });
+ }
+
+ openEnv();
+
+ openStoreReadWrite();
+
+ /*
+ * Evolve and expect that the expected number of entities are
+ * converted.
+ */
+ int nExpected = caseObj.getNRecordsExpected();
+ evolveNRead = 0;
+ evolveNConverted = 0;
+ PersistCatalog.unevolvedFormatsEncountered = false;
+ EvolveStats stats = store.evolve(config);
+ if (nExpected > 0) {
+ assertTrue(PersistCatalog.unevolvedFormatsEncountered);
+ }
+ assertTrue(stats.getNRead() == nExpected);
+ assertTrue(stats.getNConverted() == nExpected);
+ assertTrue(stats.getNConverted() >= stats.getNRead());
+ if (useEvolveListener) {
+ assertEquals(evolveNRead, stats.getNRead());
+ assertEquals(evolveNConverted, stats.getNConverted());
+ }
+
+ /* Evolve again and expect that no entities are converted. */
+ evolveNRead = 0;
+ evolveNConverted = 0;
+ PersistCatalog.unevolvedFormatsEncountered = false;
+ stats = store.evolve(config);
+ assertTrue(!PersistCatalog.unevolvedFormatsEncountered);
+ assertEquals(0, stats.getNRead());
+ assertEquals(0, stats.getNConverted());
+ if (useEvolveListener) {
+ assertTrue(evolveNRead == 0);
+ assertTrue(evolveNConverted == 0);
+ }
+
+ /* Ensure that we can read all entities without evolution. */
+ PersistCatalog.unevolvedFormatsEncountered = false;
+ caseObj.readObjects(store, false /*doUpdate*/);
+ assertTrue(!PersistCatalog.unevolvedFormatsEncountered);
+
+ /*
+ * When automatic unused type deletion is implemented in the future the
+ * oldTypesExist parameters below should be changed to false.
+ */
+
+ /* Open again and try an update. */
+ caseObj.checkEvolvedModel
+ (store.getModel(), env, true /*oldTypesExist*/);
+ caseObj.readObjects(store, true /*doUpdate*/);
+ caseObj.checkEvolvedModel
+ (store.getModel(), env, true /*oldTypesExist*/);
+ closeStore();
+
+ /* Open read-only and double check that everything is OK. */
+ openStoreReadOnly();
+ caseObj.checkEvolvedModel
+ (store.getModel(), env, true /*oldTypesExist*/);
+ caseObj.readObjects(store, false /*doUpdate*/);
+ caseObj.checkEvolvedModel
+ (store.getModel(), env, true /*oldTypesExist*/);
+ closeStore();
+
+ /* Check raw objects. */
+ openRawStore();
+ caseObj.checkEvolvedModel
+ (rawStore.getModel(), env, true /*oldTypesExist*/);
+ caseObj.readRawObjects
+ (rawStore, true /*expectEvolved*/, true /*expectUpdated*/);
+
+ /*
+ * Test copy raw object to new store via convertRawObject. In this
+ * test we can pass false for oldTypesExist because newStore starts
+ * with the new/evolved class model.
+ */
+ openNewStore();
+ caseObj.copyRawObjects(rawStore, newStore);
+ caseObj.readObjects(newStore, true /*doUpdate*/);
+ caseObj.checkEvolvedModel
+ (newStore.getModel(), env, false /*oldTypesExist*/);
+ closeNewStore();
+ closeRawStore();
+
+ closeAll();
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/EvolveTestBase.java b/test/scr024/src/com/sleepycat/persist/test/EvolveTestBase.java
new file mode 100644
index 00000000..7b97dcd3
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/EvolveTestBase.java
@@ -0,0 +1,438 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.persist.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Enumeration;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.AnnotationModel;
+import com.sleepycat.persist.model.EntityModel;
+import com.sleepycat.persist.raw.RawStore;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Base class for EvolveTest and EvolveTestInit.
+ *
+ * @author Mark Hayes
+ */
+public abstract class EvolveTestBase extends TestCase {
+
+ /*
+ * When adding a evolve test class, three places need to be changed:
+ * 1) Add the unmodified class to EvolveClass.java.original.
+ * 2) Add the modified class to EvolveClass.java.
+ * 3) Add the class name to the ALL list below as a pair of strings. The
+ * first string in each pair is the name of the original class, and the
+ * second string is the name of the evolved class or null if the evolved
+ * name is the same as the original. The index in the list identifies a
+ * test case, and the class at that position identifies the old and new
+ * class to use for the test.
+ */
+ private static final String[] ALL = {
+//*
+ "DeletedEntity1_ClassRemoved",
+ "DeletedEntity1_ClassRemoved_NoMutation",
+ "DeletedEntity2_ClassRemoved",
+ "DeletedEntity2_ClassRemoved_WithDeleter",
+ "DeletedEntity3_AnnotRemoved_NoMutation",
+ null,
+ "DeletedEntity4_AnnotRemoved_WithDeleter",
+ null,
+ "DeletedEntity5_EntityToPersist_NoMutation",
+ null,
+ "DeletedEntity6_EntityToPersist_WithDeleter",
+ null,
+ "DeletedPersist1_ClassRemoved_NoMutation",
+ null,
+ "DeletedPersist2_ClassRemoved_WithDeleter",
+ null,
+ "DeletedPersist3_AnnotRemoved_NoMutation",
+ null,
+ "DeletedPersist4_AnnotRemoved_WithDeleter",
+ null,
+ "DeletedPersist5_PersistToEntity_NoMutation",
+ null,
+ "DeletedPersist6_PersistToEntity_WithDeleter",
+ null,
+ "RenamedEntity1_NewEntityName",
+ "RenamedEntity1_NewEntityName_NoMutation",
+ "RenamedEntity2_NewEntityName",
+ "RenamedEntity2_NewEntityName_WithRenamer",
+ "DeleteSuperclass1_NoMutation",
+ null,
+ "DeleteSuperclass2_WithConverter",
+ null,
+ "DeleteSuperclass3_WithDeleter",
+ null,
+ "DeleteSuperclass4_NoFields",
+ null,
+ "DeleteSuperclass5_Top",
+ null,
+ "InsertSuperclass1_Between",
+ null,
+ "InsertSuperclass2_Top",
+ null,
+ "DisallowNonKeyField_PrimitiveToObject",
+ null,
+ "DisallowNonKeyField_ObjectToPrimitive",
+ null,
+ "DisallowNonKeyField_ObjectToSubtype",
+ null,
+ "DisallowNonKeyField_ObjectToUnrelatedSimple",
+ null,
+ "DisallowNonKeyField_ObjectToUnrelatedOther",
+ null,
+ "DisallowNonKeyField_byte2boolean",
+ null,
+ "DisallowNonKeyField_short2byte",
+ null,
+ "DisallowNonKeyField_int2short",
+ null,
+ "DisallowNonKeyField_long2int",
+ null,
+ "DisallowNonKeyField_float2long",
+ null,
+ "DisallowNonKeyField_double2float",
+ null,
+ "DisallowNonKeyField_Byte2byte",
+ null,
+ "DisallowNonKeyField_Character2char",
+ null,
+ "DisallowNonKeyField_Short2short",
+ null,
+ "DisallowNonKeyField_Integer2int",
+ null,
+ "DisallowNonKeyField_Long2long",
+ null,
+ "DisallowNonKeyField_Float2float",
+ null,
+ "DisallowNonKeyField_Double2double",
+ null,
+ "DisallowNonKeyField_float2BigInt",
+ null,
+ "DisallowNonKeyField_BigInt2long",
+ null,
+ "DisallowSecKeyField_byte2short",
+ null,
+ "DisallowSecKeyField_char2int",
+ null,
+ "DisallowSecKeyField_short2int",
+ null,
+ "DisallowSecKeyField_int2long",
+ null,
+ "DisallowSecKeyField_long2float",
+ null,
+ "DisallowSecKeyField_float2double",
+ null,
+ "DisallowSecKeyField_Byte2short2",
+ null,
+ "DisallowSecKeyField_Character2int",
+ null,
+ "DisallowSecKeyField_Short2int2",
+ null,
+ "DisallowSecKeyField_Integer2long",
+ null,
+ "DisallowSecKeyField_Long2float2",
+ null,
+ "DisallowSecKeyField_Float2double2",
+ null,
+ "DisallowSecKeyField_int2BigInt",
+ null,
+ "DisallowPriKeyField_byte2short",
+ null,
+ "DisallowPriKeyField_char2int",
+ null,
+ "DisallowPriKeyField_short2int",
+ null,
+ "DisallowPriKeyField_int2long",
+ null,
+ "DisallowPriKeyField_long2float",
+ null,
+ "DisallowPriKeyField_float2double",
+ null,
+ "DisallowPriKeyField_Byte2short2",
+ null,
+ "DisallowPriKeyField_Character2int",
+ null,
+ "DisallowPriKeyField_Short2int2",
+ null,
+ "DisallowPriKeyField_Integer2long",
+ null,
+ "DisallowPriKeyField_Long2float2",
+ null,
+ "DisallowPriKeyField_Float2double2",
+ null,
+ "DisallowPriKeyField_Long2BigInt",
+ null,
+ "DisallowCompositeKeyField_byte2short",
+ null,
+ "AllowPriKeyField_Byte2byte2",
+ null,
+ "AllowPriKeyField_byte2Byte",
+ null,
+ "AllowFieldTypeChanges",
+ null,
+ "ConvertFieldContent_Entity",
+ null,
+ "ConvertExample1_Entity",
+ null,
+ "ConvertExample2_Person",
+ null,
+ "ConvertExample3_Person",
+ null,
+ "ConvertExample3Reverse_Person",
+ null,
+ "ConvertExample4_Entity",
+ null,
+ "ConvertExample5_Entity",
+ null,
+ "AllowFieldAddDelete",
+ null,
+ "ProxiedClass_Entity",
+ null,
+ "DisallowChangeProxyFor",
+ null,
+ "DisallowDeleteProxyFor",
+ null,
+ "ArrayNameChange_Entity",
+ null,
+ "AddEnumConstant_Entity",
+ null,
+ "InsertEnumConstant_Entity",
+ null,
+ "DeleteEnumConstant_NoMutation",
+ null,
+ "DisallowChangeKeyRelate",
+ null,
+ "AllowChangeKeyMetadata",
+ null,
+ "AllowChangeKeyMetadataInSubclass",
+ null,
+ "AllowAddSecondary",
+ null,
+ "FieldAddAndConvert",
+ null,
+//*/
+ };
+
+ File envHome;
+ Environment env;
+ EntityStore store;
+ RawStore rawStore;
+ EntityStore newStore;
+ String caseClsName;
+ Class caseCls;
+ EvolveCase caseObj;
+ String caseLabel;
+
+ static TestSuite getSuite(Class testClass)
+ throws Exception {
+
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < ALL.length; i += 2) {
+ String originalClsName = ALL[i];
+ String evolvedClsName = ALL[i + 1];
+ if (evolvedClsName == null) {
+ evolvedClsName = originalClsName;
+ }
+ TestSuite baseSuite = new TestSuite(testClass);
+ Enumeration e = baseSuite.tests();
+ while (e.hasMoreElements()) {
+ EvolveTestBase test = (EvolveTestBase) e.nextElement();
+ test.init(originalClsName, evolvedClsName);
+ suite.addTest(test);
+ }
+ }
+ return suite;
+ }
+
+ private void init(String originalClsName,
+ String evolvedClsName)
+ throws Exception {
+
+ String caseClsName = useEvolvedClass() ?
+ evolvedClsName : originalClsName;
+ caseClsName = "com.sleepycat.persist.test.EvolveClasses$" +
+ caseClsName;
+
+ this.caseClsName = caseClsName;
+ this.caseCls = Class.forName(caseClsName);
+ this.caseObj = (EvolveCase) caseCls.newInstance();
+ this.caseLabel = evolvedClsName;
+ }
+
+ abstract boolean useEvolvedClass();
+
+ File getTestInitHome(boolean evolved) {
+ return new File
+ (System.getProperty("testevolvedir"),
+ (evolved ? "evolved" : "original") + '/' + caseLabel);
+ }
+
+ @Override
+ public void tearDown() {
+
+ /* Set test name for reporting; cannot be done in the ctor or setUp. */
+ setName(caseLabel + '-' + getName());
+
+ if (env != null) {
+ try {
+ closeAll();
+ } catch (Throwable e) {
+ System.out.println("During tearDown: " + e);
+ }
+ }
+ envHome = null;
+ env = null;
+ store = null;
+ caseCls = null;
+ caseObj = null;
+ caseLabel = null;
+
+ /* Do not delete log files so they can be used by 2nd phase of test. */
+ }
+
+ /**
+ * @throws FileNotFoundException from DB core.
+ */
+ void openEnv()
+ throws FileNotFoundException, DatabaseException {
+
+ EnvironmentConfig config = TestEnv.TXN.getConfig();
+ config.setAllowCreate(true);
+ env = new Environment(envHome, config);
+ }
+
+ /**
+ * Returns true if the store was opened successfully. Returns false if the
+ * store could not be opened because an exception was expected -- this is
+ * not a test failure but no further tests for an EntityStore may be run.
+ */
+ private boolean openStore(StoreConfig config)
+ throws Exception {
+
+ config.setTransactional(true);
+ config.setMutations(caseObj.getMutations());
+
+ EntityModel model = new AnnotationModel();
+ config.setModel(model);
+ caseObj.configure(model, config);
+
+ String expectException = caseObj.getStoreOpenException();
+ try {
+ store = new EntityStore(env, EvolveCase.STORE_NAME, config);
+ if (expectException != null) {
+ fail("Expected: " + expectException);
+ }
+ } catch (Exception e) {
+ if (expectException != null) {
+ //e.printStackTrace();
+ String actualMsg = e.getMessage();
+ EvolveCase.checkEquals
+ (expectException,
+ e.getClass().getName() + ": " + actualMsg);
+ return false;
+ } else {
+ throw e;
+ }
+ }
+ return true;
+ }
+
+ boolean openStoreReadOnly()
+ throws Exception {
+
+ StoreConfig config = new StoreConfig();
+ config.setReadOnly(true);
+ return openStore(config);
+ }
+
+ boolean openStoreReadWrite()
+ throws Exception {
+
+ StoreConfig config = new StoreConfig();
+ config.setAllowCreate(true);
+ return openStore(config);
+ }
+
+ void openRawStore()
+ throws DatabaseException {
+
+ StoreConfig config = new StoreConfig();
+ config.setTransactional(true);
+ rawStore = new RawStore(env, EvolveCase.STORE_NAME, config);
+ }
+
+ void closeStore()
+ throws DatabaseException {
+
+ if (store != null) {
+ store.close();
+ store = null;
+ }
+ }
+
+ void openNewStore()
+ throws Exception {
+
+ StoreConfig config = new StoreConfig();
+ config.setAllowCreate(true);
+ config.setTransactional(true);
+
+ EntityModel model = new AnnotationModel();
+ config.setModel(model);
+ caseObj.configure(model, config);
+
+ newStore = new EntityStore(env, "new", config);
+ }
+
+ void closeNewStore()
+ throws DatabaseException {
+
+ if (newStore != null) {
+ newStore.close();
+ newStore = null;
+ }
+ }
+
+ void closeRawStore()
+ throws DatabaseException {
+
+ if (rawStore != null) {
+ rawStore.close();
+ rawStore = null;
+ }
+ }
+
+ void closeEnv()
+ throws DatabaseException {
+
+ if (env != null) {
+ env.close();
+ env = null;
+ }
+ }
+
+ void closeAll()
+ throws DatabaseException {
+
+ closeStore();
+ closeRawStore();
+ closeNewStore();
+ closeEnv();
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/EvolveTestInit.java b/test/scr024/src/com/sleepycat/persist/test/EvolveTestInit.java
new file mode 100644
index 00000000..06ed2a7f
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/EvolveTestInit.java
@@ -0,0 +1,53 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.persist.test;
+
+import junit.framework.Test;
+
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * Runs part one of the EvolveTest. This part is run with the old/original
+ * version of EvolveClasses in the classpath. It creates a fresh environment
+ * and store containing instances of the original class. When EvolveTest is
+ * run, it will read/write/evolve these objects from the store created here.
+ *
+ * @author Mark Hayes
+ */
+public class EvolveTestInit extends EvolveTestBase {
+
+ public static Test suite()
+ throws Exception {
+
+ return getSuite(EvolveTestInit.class);
+ }
+
+ @Override
+ boolean useEvolvedClass() {
+ return false;
+ }
+
+ @Override
+ public void setUp() {
+ envHome = getTestInitHome(false /*evolved*/);
+ envHome.mkdirs();
+ SharedTestUtils.emptyDir(envHome);
+ }
+
+ public void testInit()
+ throws Exception {
+
+ openEnv();
+ if (!openStoreReadWrite()) {
+ fail();
+ }
+ caseObj.writeObjects(store);
+ caseObj.checkUnevolvedModel(store.getModel(), env);
+ closeAll();
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/ForeignKeyTest.java b/test/scr024/src/com/sleepycat/persist/test/ForeignKeyTest.java
new file mode 100644
index 00000000..741452cf
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/ForeignKeyTest.java
@@ -0,0 +1,329 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.DeleteAction.ABORT;
+import static com.sleepycat.persist.model.DeleteAction.CASCADE;
+import static com.sleepycat.persist.model.DeleteAction.NULLIFY;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
+
+import java.util.Enumeration;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.DeleteAction;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.util.test.TxnTestCase;
+
+/**
+ * @author Mark Hayes
+ */
+public class ForeignKeyTest extends TxnTestCase {
+
+ private static final DeleteAction[] ACTIONS = {
+ ABORT,
+ NULLIFY,
+ CASCADE,
+ };
+
+ private static final String[] ACTION_LABELS = {
+ "ABORT",
+ "NULLIFY",
+ "CASCADE",
+ };
+
+ static protected Class<?> testClass = ForeignKeyTest.class;
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < ACTIONS.length; i += 1) {
+ for (int j = 0; j < 2; j++) {
+ TestSuite txnSuite = txnTestSuite(testClass, null, null);
+ Enumeration e = txnSuite.tests();
+ while (e.hasMoreElements()) {
+ ForeignKeyTest test = (ForeignKeyTest) e.nextElement();
+ test.onDelete = ACTIONS[i];
+ test.onDeleteLabel = ACTION_LABELS[i];
+ test.useSubclass = (j == 0);
+ test.useSubclassLabel =
+ (j == 0) ? "UseSubclass" : "UseBaseclass";
+ suite.addTest(test);
+ }
+ }
+ }
+ return suite;
+ }
+
+ private EntityStore store;
+ private PrimaryIndex<String,Entity1> pri1;
+ private PrimaryIndex<String,Entity2> pri2;
+ private SecondaryIndex<String,String,Entity1> sec1;
+ private SecondaryIndex<String,String,Entity2> sec2;
+ private DeleteAction onDelete;
+ private String onDeleteLabel;
+ private boolean useSubclass;
+ private String useSubclassLabel;
+
+ @Override
+ public void tearDown()
+ throws Exception {
+
+ super.tearDown();
+ setName(getName() + '-' + onDeleteLabel + "-" + useSubclassLabel);
+ }
+
+ private void open()
+ throws DatabaseException {
+
+ StoreConfig config = new StoreConfig();
+ config.setAllowCreate(envConfig.getAllowCreate());
+ config.setTransactional(envConfig.getTransactional());
+
+ store = new EntityStore(env, "test", config);
+
+ pri1 = store.getPrimaryIndex(String.class, Entity1.class);
+ sec1 = store.getSecondaryIndex(pri1, String.class, "sk");
+ pri2 = store.getPrimaryIndex(String.class, Entity2.class);
+ sec2 = store.getSecondaryIndex
+ (pri2, String.class, "sk_" + onDeleteLabel);
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ }
+
+ public void testForeignKeys()
+ throws Exception {
+
+ open();
+ Transaction txn = txnBegin();
+
+ Entity1 o1 = new Entity1("pk1", "sk1");
+ assertNull(pri1.put(txn, o1));
+
+ assertEquals(o1, pri1.get(txn, "pk1", null));
+ assertEquals(o1, sec1.get(txn, "sk1", null));
+
+ Entity2 o2 = (useSubclass ?
+ new Entity3("pk2", "pk1", onDelete) :
+ new Entity2("pk2", "pk1", onDelete));
+ assertNull(pri2.put(txn, o2));
+
+ assertEquals(o2, pri2.get(txn, "pk2", null));
+ assertEquals(o2, sec2.get(txn, "pk1", null));
+
+ txnCommit(txn);
+ txn = txnBegin();
+
+ /*
+ * pri1 contains o1 with primary key "pk1" and index key "sk1".
+ *
+ * pri2 contains o2 with primary key "pk2" and foreign key "pk1",
+ * which is the primary key of pri1.
+ */
+ if (onDelete == ABORT) {
+
+ /* Test that we abort trying to delete a referenced key. */
+
+ try {
+ pri1.delete(txn, "pk1");
+ fail();
+ } catch (DatabaseException expected) {
+ assertTrue(!DbCompat.NEW_JE_EXCEPTIONS);
+ txnAbort(txn);
+ txn = txnBegin();
+ }
+
+ /*
+ * Test that we can put a record into store2 with a null foreign
+ * key value.
+ */
+ o2 = (useSubclass ?
+ new Entity3("pk2", null, onDelete) :
+ new Entity2("pk2", null, onDelete));
+ assertNotNull(pri2.put(txn, o2));
+ assertEquals(o2, pri2.get(txn, "pk2", null));
+
+ /*
+ * The index2 record should have been deleted since the key was set
+ * to null above.
+ */
+ assertNull(sec2.get(txn, "pk1", null));
+
+ /*
+ * Test that now we can delete the record in store1, since it is no
+ * longer referenced.
+ */
+ assertNotNull(pri1.delete(txn, "pk1"));
+ assertNull(pri1.get(txn, "pk1", null));
+ assertNull(sec1.get(txn, "sk1", null));
+
+ } else if (onDelete == NULLIFY) {
+
+ /* Delete the referenced key. */
+ assertNotNull(pri1.delete(txn, "pk1"));
+ assertNull(pri1.get(txn, "pk1", null));
+ assertNull(sec1.get(txn, "sk1", null));
+
+ /*
+ * The store2 record should still exist, but should have an empty
+ * secondary key since it was nullified.
+ */
+ o2 = pri2.get(txn, "pk2", null);
+ assertNotNull(o2);
+ assertEquals("pk2", o2.pk);
+ assertEquals(null, o2.getSk(onDelete));
+
+ } else if (onDelete == CASCADE) {
+
+ /* Delete the referenced key. */
+ assertNotNull(pri1.delete(txn, "pk1"));
+ assertNull(pri1.get(txn, "pk1", null));
+ assertNull(sec1.get(txn, "sk1", null));
+
+ /* The store2 record should have deleted also. */
+ assertNull(pri2.get(txn, "pk2", null));
+ assertNull(sec2.get(txn, "pk1", null));
+
+ } else {
+ throw new IllegalStateException();
+ }
+
+ /*
+ * Test that a foreign key value may not be used that is not present in
+ * the foreign store. "pk2" is not in store1 in this case.
+ */
+ Entity2 o3 = (useSubclass ?
+ new Entity3("pk3", "pk2", onDelete) :
+ new Entity2("pk3", "pk2", onDelete));
+ try {
+ pri2.put(txn, o3);
+ fail();
+ } catch (DatabaseException expected) {
+ assertTrue(!DbCompat.NEW_JE_EXCEPTIONS);
+ }
+
+ txnAbort(txn);
+ close();
+ }
+
+ @Entity
+ static class Entity1 {
+
+ @PrimaryKey
+ String pk;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ String sk;
+
+ private Entity1() {}
+
+ Entity1(String pk, String sk) {
+ this.pk = pk;
+ this.sk = sk;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ Entity1 o = (Entity1) other;
+ return nullOrEqual(pk, o.pk) &&
+ nullOrEqual(sk, o.sk);
+ }
+ }
+
+ @Entity
+ static class Entity2 {
+
+ @PrimaryKey
+ String pk;
+
+ @SecondaryKey(relate=ONE_TO_ONE, relatedEntity=Entity1.class,
+ onRelatedEntityDelete=ABORT)
+ String sk_ABORT;
+
+ @SecondaryKey(relate=ONE_TO_ONE, relatedEntity=Entity1.class,
+ onRelatedEntityDelete=CASCADE)
+ String sk_CASCADE;
+
+ @SecondaryKey(relate=ONE_TO_ONE, relatedEntity=Entity1.class,
+ onRelatedEntityDelete=NULLIFY)
+ String sk_NULLIFY;
+
+ private Entity2() {}
+
+ Entity2(String pk, String sk, DeleteAction action) {
+ this.pk = pk;
+ switch (action) {
+ case ABORT:
+ sk_ABORT = sk;
+ break;
+ case CASCADE:
+ sk_CASCADE = sk;
+ break;
+ case NULLIFY:
+ sk_NULLIFY = sk;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ String getSk(DeleteAction action) {
+ switch (action) {
+ case ABORT:
+ return sk_ABORT;
+ case CASCADE:
+ return sk_CASCADE;
+ case NULLIFY:
+ return sk_NULLIFY;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ Entity2 o = (Entity2) other;
+ return nullOrEqual(pk, o.pk) &&
+ nullOrEqual(sk_ABORT, o.sk_ABORT) &&
+ nullOrEqual(sk_CASCADE, o.sk_CASCADE) &&
+ nullOrEqual(sk_NULLIFY, o.sk_NULLIFY);
+ }
+ }
+
+ @Persistent
+ static class Entity3 extends Entity2 {
+ Entity3() {}
+
+ Entity3(String pk, String sk, DeleteAction action) {
+ super(pk, sk, action);
+ }
+ }
+
+ static boolean nullOrEqual(Object o1, Object o2) {
+ if (o1 == null) {
+ return o2 == null;
+ } else {
+ return o1.equals(o2);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/IndexTest.java b/test/scr024/src/com/sleepycat/persist/test/IndexTest.java
new file mode 100644
index 00000000..d4478c58
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/IndexTest.java
@@ -0,0 +1,874 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_MANY;
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import junit.framework.Test;
+
+import com.sleepycat.collections.MapEntryParameter;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.persist.EntityCursor;
+import com.sleepycat.persist.EntityIndex;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.persist.raw.RawObject;
+import com.sleepycat.persist.raw.RawStore;
+import com.sleepycat.persist.raw.RawType;
+import com.sleepycat.util.test.TxnTestCase;
+
+/**
+ * Tests EntityIndex and EntityCursor in all their permutations.
+ *
+ * @author Mark Hayes
+ */
+public class IndexTest extends TxnTestCase {
+
+ private static final int N_RECORDS = 5;
+ private static final int THREE_TO_ONE = 3;
+
+ static protected Class<?> testClass = IndexTest.class;
+
+ public static Test suite() {
+ return txnTestSuite(testClass, null,
+ null);
+ //new String[] { TxnTestCase.TXN_NULL});
+ }
+
+ private EntityStore store;
+ private PrimaryIndex<Integer,MyEntity> primary;
+ private SecondaryIndex<Integer,Integer,MyEntity> oneToOne;
+ private SecondaryIndex<Integer,Integer,MyEntity> manyToOne;
+ private SecondaryIndex<Integer,Integer,MyEntity> oneToMany;
+ private SecondaryIndex<Integer,Integer,MyEntity> manyToMany;
+ private RawStore rawStore;
+ private RawType entityType;
+ private PrimaryIndex<Object,RawObject> primaryRaw;
+ private SecondaryIndex<Object,Object,RawObject> oneToOneRaw;
+ private SecondaryIndex<Object,Object,RawObject> manyToOneRaw;
+ private SecondaryIndex<Object,Object,RawObject> oneToManyRaw;
+ private SecondaryIndex<Object,Object,RawObject> manyToManyRaw;
+
+ /**
+ * Opens the store.
+ */
+ private void open()
+ throws DatabaseException {
+
+ StoreConfig config = new StoreConfig();
+ config.setAllowCreate(envConfig.getAllowCreate());
+ config.setTransactional(envConfig.getTransactional());
+
+ store = new EntityStore(env, "test", config);
+
+ primary = store.getPrimaryIndex(Integer.class, MyEntity.class);
+ oneToOne =
+ store.getSecondaryIndex(primary, Integer.class, "oneToOne");
+ manyToOne =
+ store.getSecondaryIndex(primary, Integer.class, "manyToOne");
+ oneToMany =
+ store.getSecondaryIndex(primary, Integer.class, "oneToMany");
+ manyToMany =
+ store.getSecondaryIndex(primary, Integer.class, "manyToMany");
+
+ assertNotNull(primary);
+ assertNotNull(oneToOne);
+ assertNotNull(manyToOne);
+ assertNotNull(oneToMany);
+ assertNotNull(manyToMany);
+
+ rawStore = new RawStore(env, "test", config);
+ String clsName = MyEntity.class.getName();
+ entityType = rawStore.getModel().getRawType(clsName);
+ assertNotNull(entityType);
+
+ primaryRaw = rawStore.getPrimaryIndex(clsName);
+ oneToOneRaw = rawStore.getSecondaryIndex(clsName, "oneToOne");
+ manyToOneRaw = rawStore.getSecondaryIndex(clsName, "manyToOne");
+ oneToManyRaw = rawStore.getSecondaryIndex(clsName, "oneToMany");
+ manyToManyRaw = rawStore.getSecondaryIndex(clsName, "manyToMany");
+
+ assertNotNull(primaryRaw);
+ assertNotNull(oneToOneRaw);
+ assertNotNull(manyToOneRaw);
+ assertNotNull(oneToManyRaw);
+ assertNotNull(manyToManyRaw);
+ }
+
+ /**
+ * Closes the store.
+ */
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ store = null;
+ rawStore.close();
+ rawStore = null;
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ super.setUp();
+ }
+
+ /**
+ * The store must be closed before closing the environment.
+ */
+ @Override
+ public void tearDown()
+ throws Exception {
+
+ try {
+ if (rawStore != null) {
+ rawStore.close();
+ }
+ } catch (Throwable e) {
+ System.out.println("During tearDown: " + e);
+ }
+ try {
+ if (store != null) {
+ store.close();
+ }
+ } catch (Throwable e) {
+ System.out.println("During tearDown: " + e);
+ }
+ store = null;
+ rawStore = null;
+ super.tearDown();
+ }
+
+ /**
+ * Primary keys: {0, 1, 2, 3, 4}
+ */
+ public void testPrimary()
+ throws DatabaseException {
+
+ SortedMap<Integer,SortedSet<Integer>> expected =
+ new TreeMap<Integer,SortedSet<Integer>>();
+
+ for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
+ SortedSet<Integer> values = new TreeSet<Integer>();
+ values.add(priKey);
+ expected.put(priKey, values);
+ }
+
+ open();
+ addEntities(primary);
+ checkIndex(primary, expected, keyGetter, entityGetter);
+ checkIndex(primaryRaw, expected, rawKeyGetter, rawEntityGetter);
+
+ /* Close and reopen, then recheck indices. */
+ close();
+ open();
+ checkIndex(primary, expected, keyGetter, entityGetter);
+ checkIndex(primaryRaw, expected, rawKeyGetter, rawEntityGetter);
+
+ /* Check primary delete, last key first for variety. */
+ for (int priKey = N_RECORDS - 1; priKey >= 0; priKey -= 1) {
+ boolean useRaw = ((priKey & 1) != 0);
+ Transaction txn = txnBegin();
+ if (useRaw) {
+ primaryRaw.delete(txn, priKey);
+ } else {
+ primary.delete(txn, priKey);
+ }
+ txnCommit(txn);
+ expected.remove(priKey);
+ checkIndex(primary, expected, keyGetter, entityGetter);
+ }
+ checkAllEmpty();
+
+ /* Check PrimaryIndex put operations. */
+ MyEntity e;
+ Transaction txn = txnBegin();
+ /* put() */
+ e = primary.put(txn, new MyEntity(1));
+ assertNull(e);
+ e = primary.get(txn, 1, null);
+ assertEquals(1, e.key);
+ /* putNoReturn() */
+ primary.putNoReturn(txn, new MyEntity(2));
+ e = primary.get(txn, 2, null);
+ assertEquals(2, e.key);
+ /* putNoOverwrite */
+ assertTrue(!primary.putNoOverwrite(txn, new MyEntity(1)));
+ assertTrue(!primary.putNoOverwrite(txn, new MyEntity(2)));
+ assertTrue(primary.putNoOverwrite(txn, new MyEntity(3)));
+ e = primary.get(txn, 3, null);
+ assertEquals(3, e.key);
+ txnCommit(txn);
+ close();
+ }
+
+ /**
+ * { 0:0, 1:-1, 2:-2, 3:-3, 4:-4 }
+ */
+ public void testOneToOne()
+ throws DatabaseException {
+
+ SortedMap<Integer,SortedSet<Integer>> expected =
+ new TreeMap<Integer,SortedSet<Integer>>();
+
+ for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
+ SortedSet<Integer> values = new TreeSet<Integer>();
+ values.add(priKey);
+ Integer secKey = (-priKey);
+ expected.put(secKey, values);
+ }
+
+ open();
+ addEntities(primary);
+ checkSecondary(oneToOne, oneToOneRaw, expected);
+ checkDelete(oneToOne, oneToOneRaw, expected);
+ close();
+ }
+
+ /**
+ * { 0:0, 1:1, 2:2, 3:0, 4:1 }
+ */
+ public void testManyToOne()
+ throws DatabaseException {
+
+ SortedMap<Integer,SortedSet<Integer>> expected =
+ new TreeMap<Integer,SortedSet<Integer>>();
+
+ for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
+ Integer secKey = priKey % THREE_TO_ONE;
+ SortedSet<Integer> values = expected.get(secKey);
+ if (values == null) {
+ values = new TreeSet<Integer>();
+ expected.put(secKey, values);
+ }
+ values.add(priKey);
+ }
+
+ open();
+ addEntities(primary);
+ checkSecondary(manyToOne, manyToOneRaw, expected);
+ checkDelete(manyToOne, manyToOneRaw, expected);
+ close();
+ }
+
+ /**
+ * { 0:{}, 1:{10}, 2:{20,21}, 3:{30,31,32}, 4:{40,41,42,43}
+ */
+ public void testOneToMany()
+ throws DatabaseException {
+
+ SortedMap<Integer,SortedSet<Integer>> expected =
+ new TreeMap<Integer,SortedSet<Integer>>();
+
+ for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
+ for (int i = 0; i < priKey; i += 1) {
+ Integer secKey = (N_RECORDS * priKey) + i;
+ SortedSet<Integer> values = expected.get(secKey);
+ if (values == null) {
+ values = new TreeSet<Integer>();
+ expected.put(secKey, values);
+ }
+ values.add(priKey);
+ }
+ }
+
+ open();
+ addEntities(primary);
+ checkSecondary(oneToMany, oneToManyRaw, expected);
+ checkDelete(oneToMany, oneToManyRaw, expected);
+ close();
+ }
+
+ /**
+ * { 0:{}, 1:{0}, 2:{0,1}, 3:{0,1,2}, 4:{0,1,2,3}
+ */
+ public void testManyToMany()
+ throws DatabaseException {
+
+ SortedMap<Integer,SortedSet<Integer>> expected =
+ new TreeMap<Integer,SortedSet<Integer>>();
+
+ for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
+ for (int i = 0; i < priKey; i += 1) {
+ Integer secKey = i;
+ SortedSet<Integer> values = expected.get(secKey);
+ if (values == null) {
+ values = new TreeSet<Integer>();
+ expected.put(secKey, values);
+ }
+ values.add(priKey);
+ }
+ }
+
+ open();
+ addEntities(primary);
+ checkSecondary(manyToMany, manyToManyRaw, expected);
+ checkDelete(manyToMany, manyToManyRaw, expected);
+ close();
+ }
+
+ private void addEntities(PrimaryIndex<Integer,MyEntity> primary)
+ throws DatabaseException {
+
+ Transaction txn = txnBegin();
+ for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
+ MyEntity prev = primary.put(txn, new MyEntity(priKey));
+ assertNull(prev);
+ }
+ txnCommit(txn);
+ }
+
+ private void checkDelete(SecondaryIndex<Integer,Integer,MyEntity> index,
+ SecondaryIndex<Object,Object,RawObject> indexRaw,
+ SortedMap<Integer,SortedSet<Integer>> expected)
+ throws DatabaseException {
+
+ SortedMap<Integer,SortedSet<Integer>> expectedSubIndex =
+ new TreeMap<Integer,SortedSet<Integer>>();
+
+ while (expected.size() > 0) {
+ Integer delSecKey = expected.firstKey();
+ SortedSet<Integer> deletedPriKeys = expected.remove(delSecKey);
+ for (SortedSet<Integer> priKeys : expected.values()) {
+ priKeys.removeAll(deletedPriKeys);
+ }
+ Transaction txn = txnBegin();
+ boolean deleted = index.delete(txn, delSecKey);
+ assertEquals(deleted, !deletedPriKeys.isEmpty());
+ deleted = index.delete(txn, delSecKey);
+ assertTrue(!deleted);
+ assertNull(index.get(txn, delSecKey, null));
+ txnCommit(txn);
+ checkSecondary(index, indexRaw, expected);
+ }
+
+ /*
+ * Delete remaining records so that the primary index is empty. Use
+ * the RawStore for variety.
+ */
+ Transaction txn = txnBegin();
+ for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
+ primaryRaw.delete(txn, priKey);
+ }
+ txnCommit(txn);
+ checkAllEmpty();
+ }
+
+ private void checkSecondary(SecondaryIndex<Integer,Integer,MyEntity> index,
+ SecondaryIndex<Object,Object,RawObject>
+ indexRaw,
+ SortedMap<Integer,SortedSet<Integer>> expected)
+ throws DatabaseException {
+
+ checkIndex(index, expected, keyGetter, entityGetter);
+ checkIndex(index.keysIndex(), expected, keyGetter, keyGetter);
+
+ checkIndex(indexRaw, expected, rawKeyGetter, rawEntityGetter);
+ checkIndex(indexRaw.keysIndex(), expected, rawKeyGetter, rawKeyGetter);
+
+ SortedMap<Integer,SortedSet<Integer>> expectedSubIndex =
+ new TreeMap<Integer,SortedSet<Integer>>();
+
+ for (Integer secKey : expected.keySet()) {
+ expectedSubIndex.clear();
+ for (Integer priKey : expected.get(secKey)) {
+ SortedSet<Integer> values = new TreeSet<Integer>();
+ values.add(priKey);
+ expectedSubIndex.put(priKey, values);
+ }
+ checkIndex(index.subIndex(secKey),
+ expectedSubIndex,
+ keyGetter,
+ entityGetter);
+ checkIndex(indexRaw.subIndex(secKey),
+ expectedSubIndex,
+ rawKeyGetter,
+ rawEntityGetter);
+ }
+ }
+
+ private <K,V> void checkIndex(EntityIndex<K,V> index,
+ SortedMap<Integer,SortedSet<Integer>>
+ expected,
+ Getter<K> kGetter,
+ Getter<V> vGetter)
+ throws DatabaseException {
+
+ SortedMap<K,V> map = index.sortedMap();
+
+ Transaction txn = txnBegin();
+ for (int i : expected.keySet()) {
+ K k = kGetter.fromInt(i);
+ SortedSet<Integer> dups = expected.get(i);
+ if (dups.isEmpty()) {
+
+ /* EntityIndex */
+ V v = index.get(txn, k, null);
+ assertNull(v);
+ assertTrue(!index.contains(txn, k, null));
+
+ /* Map/Collection */
+ v = map.get(i);
+ assertNull(v);
+ assertTrue(!map.containsKey(i));
+ } else {
+ int j = dups.first();
+
+ /* EntityIndex */
+ V v = index.get(txn, k, null);
+ assertNotNull(v);
+ assertEquals(j, vGetter.getKey(v));
+ assertTrue(index.contains(txn, k, null));
+
+ /* Map/Collection */
+ v = map.get(i);
+ assertNotNull(v);
+ assertEquals(j, vGetter.getKey(v));
+ assertTrue(map.containsKey(i));
+ assertTrue("" + i + ' ' + j + ' ' + v + ' ' + map,
+ map.containsValue(v));
+ assertTrue(map.keySet().contains(i));
+ assertTrue(map.values().contains(v));
+ assertTrue
+ (map.entrySet().contains(new MapEntryParameter(i, v)));
+ }
+ }
+ txnCommit(txn);
+
+ int keysSize = expandKeySize(expected);
+ int valuesSize = expandValueSize(expected);
+
+ /* EntityIndex.count */
+ assertEquals("keysSize=" + keysSize, valuesSize, index.count());
+
+ /* Map/Collection size */
+ assertEquals(valuesSize, map.size());
+ assertEquals(valuesSize, map.values().size());
+ assertEquals(valuesSize, map.entrySet().size());
+ assertEquals(keysSize, map.keySet().size());
+
+ /* Map/Collection isEmpty */
+ assertEquals(valuesSize == 0, map.isEmpty());
+ assertEquals(valuesSize == 0, map.values().isEmpty());
+ assertEquals(valuesSize == 0, map.entrySet().isEmpty());
+ assertEquals(keysSize == 0, map.keySet().isEmpty());
+
+ txn = txnBeginCursor();
+
+ /* Unconstrained cursors. */
+ checkCursor
+ (index.keys(txn, null),
+ map.keySet(), true,
+ expandKeys(expected), kGetter);
+ checkCursor
+ (index.entities(txn, null),
+ map.values(), false,
+ expandValues(expected), vGetter);
+
+ /* Range cursors. */
+ if (expected.isEmpty()) {
+ checkOpenRanges(txn, 0, index, expected, kGetter, vGetter);
+ checkClosedRanges(txn, 0, 1, index, expected, kGetter, vGetter);
+ } else {
+ int firstKey = expected.firstKey();
+ int lastKey = expected.lastKey();
+ for (int i = firstKey - 1; i <= lastKey + 1; i += 1) {
+ checkOpenRanges(txn, i, index, expected, kGetter, vGetter);
+ int j = i + 1;
+ if (j < lastKey + 1) {
+ checkClosedRanges
+ (txn, i, j, index, expected, kGetter, vGetter);
+ }
+ }
+ }
+
+ txnCommit(txn);
+ }
+
+ private <K,V> void checkOpenRanges(Transaction txn, int i,
+ EntityIndex<K,V> index,
+ SortedMap<Integer,SortedSet<Integer>>
+ expected,
+ Getter<K> kGetter,
+ Getter<V> vGetter)
+ throws DatabaseException {
+
+ SortedMap<K,V> map = index.sortedMap();
+ SortedMap<Integer,SortedSet<Integer>> rangeExpected;
+ K k = kGetter.fromInt(i);
+ K kPlusOne = kGetter.fromInt(i + 1);
+
+ /* Head range exclusive. */
+ rangeExpected = expected.headMap(i);
+ checkCursor
+ (index.keys(txn, null, false, k, false, null),
+ map.headMap(k).keySet(), true,
+ expandKeys(rangeExpected), kGetter);
+ checkCursor
+ (index.entities(txn, null, false, k, false, null),
+ map.headMap(k).values(), false,
+ expandValues(rangeExpected), vGetter);
+
+ /* Head range inclusive. */
+ rangeExpected = expected.headMap(i + 1);
+ checkCursor
+ (index.keys(txn, null, false, k, true, null),
+ map.headMap(kPlusOne).keySet(), true,
+ expandKeys(rangeExpected), kGetter);
+ checkCursor
+ (index.entities(txn, null, false, k, true, null),
+ map.headMap(kPlusOne).values(), false,
+ expandValues(rangeExpected), vGetter);
+
+ /* Tail range exclusive. */
+ rangeExpected = expected.tailMap(i + 1);
+ checkCursor
+ (index.keys(txn, k, false, null, false, null),
+ map.tailMap(kPlusOne).keySet(), true,
+ expandKeys(rangeExpected), kGetter);
+ checkCursor
+ (index.entities(txn, k, false, null, false, null),
+ map.tailMap(kPlusOne).values(), false,
+ expandValues(rangeExpected), vGetter);
+
+ /* Tail range inclusive. */
+ rangeExpected = expected.tailMap(i);
+ checkCursor
+ (index.keys(txn, k, true, null, false, null),
+ map.tailMap(k).keySet(), true,
+ expandKeys(rangeExpected), kGetter);
+ checkCursor
+ (index.entities(txn, k, true, null, false, null),
+ map.tailMap(k).values(), false,
+ expandValues(rangeExpected), vGetter);
+ }
+
+ private <K,V> void checkClosedRanges(Transaction txn, int i, int j,
+ EntityIndex<K,V> index,
+ SortedMap<Integer,SortedSet<Integer>>
+ expected,
+ Getter<K> kGetter,
+ Getter<V> vGetter)
+ throws DatabaseException {
+
+ SortedMap<K,V> map = index.sortedMap();
+ SortedMap<Integer,SortedSet<Integer>> rangeExpected;
+ K k = kGetter.fromInt(i);
+ K kPlusOne = kGetter.fromInt(i + 1);
+ K l = kGetter.fromInt(j);
+ K lPlusOne = kGetter.fromInt(j + 1);
+
+ /* Sub range exclusive. */
+ rangeExpected = expected.subMap(i + 1, j);
+ checkCursor
+ (index.keys(txn, k, false, l, false, null),
+ map.subMap(kPlusOne, l).keySet(), true,
+ expandKeys(rangeExpected), kGetter);
+ checkCursor
+ (index.entities(txn, k, false, l, false, null),
+ map.subMap(kPlusOne, l).values(), false,
+ expandValues(rangeExpected), vGetter);
+
+ /* Sub range inclusive. */
+ rangeExpected = expected.subMap(i, j + 1);
+ checkCursor
+ (index.keys(txn, k, true, l, true, null),
+ map.subMap(k, lPlusOne).keySet(), true,
+ expandKeys(rangeExpected), kGetter);
+ checkCursor
+ (index.entities(txn, k, true, l, true, null),
+ map.subMap(k, lPlusOne).values(), false,
+ expandValues(rangeExpected), vGetter);
+ }
+
+ private List<List<Integer>>
+ expandKeys(SortedMap<Integer,SortedSet<Integer>> map) {
+
+ List<List<Integer>> list = new ArrayList<List<Integer>>();
+ for (Integer key : map.keySet()) {
+ SortedSet<Integer> values = map.get(key);
+ List<Integer> dups = new ArrayList<Integer>();
+ for (int i = 0; i < values.size(); i += 1) {
+ dups.add(key);
+ }
+ list.add(dups);
+ }
+ return list;
+ }
+
+ private List<List<Integer>>
+ expandValues(SortedMap<Integer,SortedSet<Integer>> map) {
+
+ List<List<Integer>> list = new ArrayList<List<Integer>>();
+ for (SortedSet<Integer> values : map.values()) {
+ list.add(new ArrayList<Integer>(values));
+ }
+ return list;
+ }
+
+ private int expandKeySize(SortedMap<Integer,SortedSet<Integer>> map) {
+
+ int size = 0;
+ for (SortedSet<Integer> values : map.values()) {
+ if (values.size() > 0) {
+ size += 1;
+ }
+ }
+ return size;
+ }
+
+ private int expandValueSize(SortedMap<Integer,SortedSet<Integer>> map) {
+
+ int size = 0;
+ for (SortedSet<Integer> values : map.values()) {
+ size += values.size();
+ }
+ return size;
+ }
+
+ private <T> void checkCursor(EntityCursor<T> cursor,
+ Collection<T> collection,
+ boolean collectionIsKeySet,
+ List<List<Integer>> expected,
+ Getter<T> getter)
+ throws DatabaseException {
+
+ boolean first;
+ boolean firstDup;
+ Iterator<T> iterator = collection.iterator();
+
+ for (List<Integer> dups : expected) {
+ for (int i : dups) {
+ T o = cursor.next();
+ assertNotNull(o);
+ assertEquals(i, getter.getKey(o));
+ /* Value iterator over duplicates. */
+ if (!collectionIsKeySet) {
+ assertTrue(iterator.hasNext());
+ o = iterator.next();
+ assertNotNull(o);
+ assertEquals(i, getter.getKey(o));
+ }
+ }
+ }
+
+ first = true;
+ for (List<Integer> dups : expected) {
+ firstDup = true;
+ for (int i : dups) {
+ T o = first ? cursor.first()
+ : (firstDup ? cursor.next() : cursor.nextDup());
+ assertNotNull(o);
+ assertEquals(i, getter.getKey(o));
+ first = false;
+ firstDup = false;
+ }
+ }
+
+ first = true;
+ for (List<Integer> dups : expected) {
+ if (!dups.isEmpty()) {
+ int i = dups.get(0);
+ T o = first ? cursor.first() : cursor.nextNoDup();
+ assertNotNull(o);
+ assertEquals(i, getter.getKey(o));
+ /* Key iterator over non-duplicates. */
+ if (collectionIsKeySet) {
+ assertTrue(iterator.hasNext());
+ o = iterator.next();
+ assertNotNull(o);
+ assertEquals(i, getter.getKey(o));
+ }
+ first = false;
+ }
+ }
+
+ List<List<Integer>> reversed = new ArrayList<List<Integer>>();
+ for (List<Integer> dups : expected) {
+ ArrayList<Integer> reversedDups = new ArrayList<Integer>(dups);
+ Collections.reverse(reversedDups);
+ reversed.add(reversedDups);
+ }
+ Collections.reverse(reversed);
+
+ first = true;
+ for (List<Integer> dups : reversed) {
+ for (int i : dups) {
+ T o = first ? cursor.last() : cursor.prev();
+ assertNotNull(o);
+ assertEquals(i, getter.getKey(o));
+ first = false;
+ }
+ }
+
+ first = true;
+ for (List<Integer> dups : reversed) {
+ firstDup = true;
+ for (int i : dups) {
+ T o = first ? cursor.last()
+ : (firstDup ? cursor.prev() : cursor.prevDup());
+ assertNotNull(o);
+ assertEquals(i, getter.getKey(o));
+ first = false;
+ firstDup = false;
+ }
+ }
+
+ first = true;
+ for (List<Integer> dups : reversed) {
+ if (!dups.isEmpty()) {
+ int i = dups.get(0);
+ T o = first ? cursor.last() : cursor.prevNoDup();
+ assertNotNull(o);
+ assertEquals(i, getter.getKey(o));
+ first = false;
+ }
+ }
+
+ cursor.close();
+ }
+
+ private void checkAllEmpty()
+ throws DatabaseException {
+
+ checkEmpty(primary);
+ checkEmpty(oneToOne);
+ checkEmpty(oneToMany);
+ checkEmpty(manyToOne);
+ checkEmpty(manyToMany);
+ }
+
+ private <K,V> void checkEmpty(EntityIndex<K,V> index)
+ throws DatabaseException {
+
+ EntityCursor<K> keys = index.keys();
+ assertNull(keys.next());
+ assertTrue(!keys.iterator().hasNext());
+ keys.close();
+ EntityCursor<V> entities = index.entities();
+ assertNull(entities.next());
+ assertTrue(!entities.iterator().hasNext());
+ entities.close();
+ }
+
+ private interface Getter<T> {
+ int getKey(T o);
+ T fromInt(int i);
+ }
+
+ private static Getter<MyEntity> entityGetter =
+ new Getter<MyEntity>() {
+ public int getKey(MyEntity o) {
+ return o.key;
+ }
+ public MyEntity fromInt(int i) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ private static Getter<Integer> keyGetter =
+ new Getter<Integer>() {
+ public int getKey(Integer o) {
+ return o;
+ }
+ public Integer fromInt(int i) {
+ return Integer.valueOf(i);
+ }
+ };
+
+ private static Getter<RawObject> rawEntityGetter =
+ new Getter<RawObject>() {
+ public int getKey(RawObject o) {
+ Object val = o.getValues().get("key");
+ return ((Integer) val).intValue();
+ }
+ public RawObject fromInt(int i) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ private static Getter<Object> rawKeyGetter =
+ new Getter<Object>() {
+ public int getKey(Object o) {
+ return ((Integer) o).intValue();
+ }
+ public Object fromInt(int i) {
+ return Integer.valueOf(i);
+ }
+ };
+
+ @Entity
+ private static class MyEntity {
+
+ @PrimaryKey
+ private int key;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ private int oneToOne;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private int manyToOne;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private Set<Integer> oneToMany = new TreeSet<Integer>();
+
+ @SecondaryKey(relate=MANY_TO_MANY)
+ private Set<Integer> manyToMany = new TreeSet<Integer>();
+
+ private MyEntity() {}
+
+ private MyEntity(int key) {
+
+ /* example keys: {0, 1, 2, 3, 4} */
+ this.key = key;
+
+ /* { 0:0, 1:-1, 2:-2, 3:-3, 4:-4 } */
+ oneToOne = -key;
+
+ /* { 0:0, 1:1, 2:2, 3:0, 4:1 } */
+ manyToOne = key % THREE_TO_ONE;
+
+ /* { 0:{}, 1:{10}, 2:{20,21}, 3:{30,31,32}, 4:{40,41,42,43} */
+ for (int i = 0; i < key; i += 1) {
+ oneToMany.add((N_RECORDS * key) + i);
+ }
+
+ /* { 0:{}, 1:{0}, 2:{0,1}, 3:{0,1,2}, 4:{0,1,2,3} */
+ for (int i = 0; i < key; i += 1) {
+ manyToMany.add(i);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "MyEntity " + key;
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/JoinTest.java b/test/scr024/src/com/sleepycat/persist/test/JoinTest.java
new file mode 100644
index 00000000..b77d37db
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/JoinTest.java
@@ -0,0 +1,176 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.persist.EntityJoin;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.ForwardCursor;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.util.test.TxnTestCase;
+
+/**
+ * @author Mark Hayes
+ */
+public class JoinTest extends TxnTestCase {
+
+ private static final int N_RECORDS = 5;
+
+ static protected Class<?> testClass = JoinTest.class;
+
+ public static Test suite() {
+ return txnTestSuite(testClass, null, null);
+ }
+
+ private EntityStore store;
+ private PrimaryIndex<Integer,MyEntity> primary;
+ private SecondaryIndex<Integer,Integer,MyEntity> sec1;
+ private SecondaryIndex<Integer,Integer,MyEntity> sec2;
+ private SecondaryIndex<Integer,Integer,MyEntity> sec3;
+
+ /**
+ * Opens the store.
+ */
+ private void open()
+ throws DatabaseException {
+
+ StoreConfig config = new StoreConfig();
+ config.setAllowCreate(envConfig.getAllowCreate());
+ config.setTransactional(envConfig.getTransactional());
+
+ store = new EntityStore(env, "test", config);
+
+ primary = store.getPrimaryIndex(Integer.class, MyEntity.class);
+ sec1 = store.getSecondaryIndex(primary, Integer.class, "k1");
+ sec2 = store.getSecondaryIndex(primary, Integer.class, "k2");
+ sec3 = store.getSecondaryIndex(primary, Integer.class, "k3");
+ }
+
+ /**
+ * Closes the store.
+ */
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ }
+
+ public void testJoin()
+ throws DatabaseException {
+
+ open();
+
+ /*
+ * Primary keys: { 0, 1, 2, 3, 4 }
+ * Secondary k1: { 0:0, 0:1, 0:2, 0:3, 0:4 }
+ * Secondary k2: { 0:0, 1:1, 0:2, 1:3, 0:4 }
+ * Secondary k3: { 0:0, 1:1, 2:2, 0:3, 1:4 }
+ */
+ Transaction txn = txnBegin();
+ for (int i = 0; i < N_RECORDS; i += 1) {
+ MyEntity e = new MyEntity(i, 0, i % 2, i % 3);
+ boolean ok = primary.putNoOverwrite(txn, e);
+ assertTrue(ok);
+ }
+ txnCommit(txn);
+
+ /*
+ * k1, k2, k3, -> { primary keys }
+ * -1 means don't include the key in the join.
+ */
+ doJoin( 0, 0, 0, new int[] { 0 });
+ doJoin( 0, 0, 1, new int[] { 4 });
+ doJoin( 0, 0, -1, new int[] { 0, 2, 4 });
+ doJoin(-1, 1, 1, new int[] { 1 });
+ doJoin(-1, 2, 2, new int[] { });
+ doJoin(-1, -1, 2, new int[] { 2 });
+
+ close();
+ }
+
+ private void doJoin(int k1, int k2, int k3, int[] expectKeys)
+ throws DatabaseException {
+
+ List<Integer> expect = new ArrayList<Integer>();
+ for (int i : expectKeys) {
+ expect.add(i);
+ }
+ EntityJoin join = new EntityJoin(primary);
+ if (k1 >= 0) {
+ join.addCondition(sec1, k1);
+ }
+ if (k2 >= 0) {
+ join.addCondition(sec2, k2);
+ }
+ if (k3 >= 0) {
+ join.addCondition(sec3, k3);
+ }
+ List<Integer> found;
+ Transaction txn = txnBegin();
+
+ /* Keys */
+ found = new ArrayList<Integer>();
+ ForwardCursor<Integer> keys = join.keys(txn, null);
+ for (int i : keys) {
+ found.add(i);
+ }
+ keys.close();
+ assertEquals(expect, found);
+
+ /* Entities */
+ found = new ArrayList<Integer>();
+ ForwardCursor<MyEntity> entities = join.entities(txn, null);
+ for (MyEntity e : entities) {
+ found.add(e.id);
+ }
+ entities.close();
+ assertEquals(expect, found);
+
+ txnCommit(txn);
+ }
+
+ @Entity
+ private static class MyEntity {
+ @PrimaryKey
+ int id;
+ @SecondaryKey(relate=MANY_TO_ONE)
+ int k1;
+ @SecondaryKey(relate=MANY_TO_ONE)
+ int k2;
+ @SecondaryKey(relate=MANY_TO_ONE)
+ int k3;
+
+ private MyEntity() {}
+
+ MyEntity(int id, int k1, int k2, int k3) {
+ this.id = id;
+ this.k1 = k1;
+ this.k2 = k2;
+ this.k3 = k3;
+ }
+
+ @Override
+ public String toString() {
+ return "MyEntity " + id + ' ' + k1 + ' ' + k2 + ' ' + k3;
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/NegativeTest.java b/test/scr024/src/com/sleepycat/persist/test/NegativeTest.java
new file mode 100644
index 00000000..55034512
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/NegativeTest.java
@@ -0,0 +1,644 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
+import static com.sleepycat.persist.model.DeleteAction.NULLIFY;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import junit.framework.Test;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.AnnotationModel;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.KeyField;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PersistentProxy;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.util.test.TxnTestCase;
+
+/**
+ * Negative tests.
+ *
+ * @author Mark Hayes
+ */
+public class NegativeTest extends TxnTestCase {
+
+ static protected Class<?> testClass = NegativeTest.class;
+
+ public static Test suite() {
+ return txnTestSuite(testClass, null, null);
+ }
+
+ private EntityStore store;
+
+ private void open()
+ throws DatabaseException {
+
+ open(null);
+ }
+
+ private void open(Class<ProxyExtendsEntity> clsToRegister)
+ throws DatabaseException {
+
+ StoreConfig config = new StoreConfig();
+ config.setAllowCreate(envConfig.getAllowCreate());
+ config.setTransactional(envConfig.getTransactional());
+
+ if (clsToRegister != null) {
+ AnnotationModel model = new AnnotationModel();
+ model.registerClass(clsToRegister);
+ config.setModel(model);
+ }
+
+ store = new EntityStore(env, "test", config);
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ store = null;
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ super.setUp();
+ }
+
+ @Override
+ public void tearDown()
+ throws Exception {
+
+ if (store != null) {
+ try {
+ store.close();
+ } catch (Throwable e) {
+ System.out.println("tearDown: " + e);
+ }
+ store = null;
+ }
+ super.tearDown();
+ }
+
+ public void testBadKeyClass1()
+ throws DatabaseException {
+
+ open();
+ try {
+ store.getPrimaryIndex(BadKeyClass1.class, UseBadKeyClass1.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf("@KeyField") >= 0);
+ }
+ close();
+ }
+
+ /** Missing @KeyField in composite key class. */
+ @Persistent
+ static class BadKeyClass1 {
+
+ private int f1;
+ }
+
+ @Entity
+ static class UseBadKeyClass1 {
+
+ @PrimaryKey
+ private BadKeyClass1 f1 = new BadKeyClass1();
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ private BadKeyClass1 f2 = new BadKeyClass1();
+ }
+
+ public void testBadSequenceKeys()
+ throws DatabaseException {
+
+ open();
+ try {
+ store.getPrimaryIndex(Boolean.class, BadSequenceKeyEntity1.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("Type not allowed for sequence") >= 0);
+ }
+ try {
+ store.getPrimaryIndex(BadSequenceKeyEntity2.Key.class,
+ BadSequenceKeyEntity2.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("Type not allowed for sequence") >= 0);
+ }
+ try {
+ store.getPrimaryIndex(BadSequenceKeyEntity3.Key.class,
+ BadSequenceKeyEntity3.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("A composite key class used with a sequence may contain " +
+ "only a single integer key field")>= 0);
+ }
+ close();
+ }
+
+ /** Boolean not allowed for sequence key. */
+ @Entity
+ static class BadSequenceKeyEntity1 {
+
+ @PrimaryKey(sequence="X")
+ private boolean key;
+ }
+
+ /** Composite key with non-integer field not allowed for sequence key. */
+ @Entity
+ static class BadSequenceKeyEntity2 {
+
+ @PrimaryKey(sequence="X")
+ private Key key;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ boolean key;
+ }
+ }
+
+ /** Composite key with multiple key fields not allowed for sequence key. */
+ @Entity
+ static class BadSequenceKeyEntity3 {
+
+ @PrimaryKey(sequence="X")
+ private Key key;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ int key;
+ @KeyField(2)
+ int key2;
+ }
+ }
+
+ /**
+ * A proxied object may not current contain a field that references the
+ * parent proxy. [#15815]
+ */
+ public void testProxyNestedRef()
+ throws DatabaseException {
+
+ open();
+ PrimaryIndex<Integer,ProxyNestedRef> index = store.getPrimaryIndex
+ (Integer.class, ProxyNestedRef.class);
+ ProxyNestedRef entity = new ProxyNestedRef();
+ entity.list.add(entity.list);
+ try {
+ index.put(entity);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("Cannot embed a reference to a proxied object") >= 0);
+ }
+ close();
+ }
+
+ @Entity
+ static class ProxyNestedRef {
+
+ @PrimaryKey
+ private int key;
+
+ ArrayList<Object> list = new ArrayList<Object>();
+ }
+
+ /**
+ * Disallow primary keys on entity subclasses. [#15757]
+ */
+ public void testEntitySubclassWithPrimaryKey()
+ throws DatabaseException {
+
+ open();
+ PrimaryIndex<Integer,EntitySuperClass> index = store.getPrimaryIndex
+ (Integer.class, EntitySuperClass.class);
+ EntitySuperClass e1 = new EntitySuperClass(1, "one");
+ index.put(e1);
+ assertEquals(e1, index.get(1));
+ EntitySubClass e2 = new EntitySubClass(2, "two", "foo", 9);
+ try {
+ index.put(e2);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.getMessage().contains
+ ("PrimaryKey may not appear on an Entity subclass"));
+ }
+ assertEquals(e1, index.get(1));
+ close();
+ }
+
+ @Entity
+ static class EntitySuperClass {
+
+ @PrimaryKey
+ private int x;
+
+ private String y;
+
+ EntitySuperClass(int x, String y) {
+ assert y != null;
+ this.x = x;
+ this.y = y;
+ }
+
+ private EntitySuperClass() {}
+
+ @Override
+ public String toString() {
+ return "x=" + x + " y=" + y;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof EntitySuperClass) {
+ EntitySuperClass o = (EntitySuperClass) other;
+ return x == o.x && y.equals(o.y);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ @Persistent
+ static class EntitySubClass extends EntitySuperClass {
+
+ @PrimaryKey
+ private String foo;
+
+ private int z;
+
+ EntitySubClass(int x, String y, String foo, int z) {
+ super(x, y);
+ assert foo != null;
+ this.foo = foo;
+ this.z = z;
+ }
+
+ private EntitySubClass() {}
+
+ @Override
+ public String toString() {
+ return super.toString() + " z=" + z;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof EntitySubClass) {
+ EntitySubClass o = (EntitySubClass) other;
+ return super.equals(o) && z == o.z;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Disallow embedded entity classes and subclasses. [#16077]
+ */
+ public void testEmbeddedEntity()
+ throws DatabaseException {
+
+ open();
+ PrimaryIndex<Integer,EmbeddingEntity> index = store.getPrimaryIndex
+ (Integer.class, EmbeddingEntity.class);
+ EmbeddingEntity e1 = new EmbeddingEntity(1, null);
+ index.put(e1);
+ assertEquals(e1, index.get(1));
+
+ EmbeddingEntity e2 =
+ new EmbeddingEntity(2, new EntitySuperClass(2, "two"));
+ try {
+ index.put(e2);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.getMessage().contains
+ ("References to entities are not allowed"));
+ }
+
+ EmbeddingEntity e3 = new EmbeddingEntity
+ (3, new EmbeddedEntitySubClass(3, "three", "foo", 9));
+ try {
+ index.put(e3);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.toString(), e.getMessage().contains
+ ("References to entities are not allowed"));
+ }
+
+ assertEquals(e1, index.get(1));
+ close();
+ }
+
+ @Entity
+ static class EmbeddingEntity {
+
+ @PrimaryKey
+ private int x;
+
+ private EntitySuperClass y;
+
+ EmbeddingEntity(int x, EntitySuperClass y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ private EmbeddingEntity() {}
+
+ @Override
+ public String toString() {
+ return "x=" + x + " y=" + y;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof EmbeddingEntity) {
+ EmbeddingEntity o = (EmbeddingEntity) other;
+ return x == o.x &&
+ ((y == null) ? (o.y == null) : y.equals(o.y));
+ } else {
+ return false;
+ }
+ }
+ }
+
+ @Persistent
+ static class EmbeddedEntitySubClass extends EntitySuperClass {
+
+ private String foo;
+
+ private int z;
+
+ EmbeddedEntitySubClass(int x, String y, String foo, int z) {
+ super(x, y);
+ assert foo != null;
+ this.foo = foo;
+ this.z = z;
+ }
+
+ private EmbeddedEntitySubClass() {}
+
+ @Override
+ public String toString() {
+ return super.toString() + " z=" + z;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof EmbeddedEntitySubClass) {
+ EmbeddedEntitySubClass o = (EmbeddedEntitySubClass) other;
+ return super.equals(o) && z == o.z;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Disallow SecondaryKey collection with no type parameter. [#15950]
+ */
+ public void testTypelessKeyCollection()
+ throws DatabaseException {
+
+ open();
+ try {
+ store.getPrimaryIndex
+ (Integer.class, TypelessKeyCollectionEntity.class);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.toString(), e.getMessage().contains
+ ("Collection typed secondary key field must have a " +
+ "single generic type argument and a wildcard or type " +
+ "bound is not allowed"));
+ }
+ close();
+ }
+
+ @Entity
+ static class TypelessKeyCollectionEntity {
+
+ @PrimaryKey
+ private int x;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private Collection keys = new ArrayList();
+
+ TypelessKeyCollectionEntity(int x) {
+ this.x = x;
+ }
+
+ private TypelessKeyCollectionEntity() {}
+ }
+
+ /**
+ * Disallow a persistent proxy that extends an entity. [#15950]
+ */
+ public void testProxyEntity()
+ throws DatabaseException {
+
+ try {
+ open(ProxyExtendsEntity.class);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.toString(), e.getMessage().contains
+ ("A proxy may not be an entity"));
+ }
+ }
+
+ @Persistent(proxyFor=BigDecimal.class)
+ static class ProxyExtendsEntity
+ extends EntitySuperClass
+ implements PersistentProxy<BigDecimal> {
+
+ private String rep;
+
+ public BigDecimal convertProxy() {
+ return new BigDecimal(rep);
+ }
+
+ public void initializeProxy(BigDecimal o) {
+ rep = o.toString();
+ }
+ }
+
+ /**
+ * Wrapper type not allowed for nullified foreign key.
+ */
+ public void testBadNullifyKey()
+ throws DatabaseException {
+
+ open();
+ try {
+ store.getPrimaryIndex(Integer.class, BadNullifyKeyEntity1.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("NULLIFY may not be used with primitive fields") >= 0);
+ }
+ close();
+ }
+
+ @Entity
+ static class BadNullifyKeyEntity1 {
+
+ @PrimaryKey
+ private int key;
+
+ @SecondaryKey(relate=ONE_TO_ONE,
+ relatedEntity=BadNullifyKeyEntity2.class,
+ onRelatedEntityDelete=NULLIFY)
+ private int secKey; // Should be Integer, not int.
+ }
+
+ @Entity
+ static class BadNullifyKeyEntity2 {
+
+ @PrimaryKey
+ private int key;
+ }
+
+ /**
+ * @Persistent not allowed on an enum.
+ */
+ public void testPersistentEnum()
+ throws DatabaseException {
+
+ open();
+ try {
+ store.getPrimaryIndex(Integer.class, PersistentEnumEntity.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("not allowed for enum, interface, or primitive") >= 0);
+ }
+ close();
+ }
+
+ @Entity
+ static class PersistentEnumEntity {
+
+ @PrimaryKey
+ private int key;
+
+ @Persistent
+ enum MyEnum {X, Y, Z};
+
+ MyEnum f1;
+ }
+
+ /**
+ * Disallow a reference to an interface marked @Persistent.
+ */
+ public void testPersistentInterface()
+ throws DatabaseException {
+
+ open();
+ try {
+ store.getPrimaryIndex(Integer.class,
+ PersistentInterfaceEntity1.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("not allowed for enum, interface, or primitive") >= 0);
+ }
+ close();
+ }
+
+ @Entity
+ static class PersistentInterfaceEntity1 {
+
+ @PrimaryKey
+ private int key;
+
+ @SecondaryKey(relate=ONE_TO_ONE,
+ relatedEntity=PersistentInterfaceEntity2.class)
+ private int secKey; // Should be Integer, not int.
+ }
+
+ @Persistent
+ interface PersistentInterfaceEntity2 {
+ }
+
+ /**
+ * Disallow reference to @Persistent inner class.
+ */
+ public void testPersistentInnerClass()
+ throws DatabaseException {
+
+ open();
+ try {
+ store.getPrimaryIndex(Integer.class,
+ PersistentInnerClassEntity1.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("Inner classes not allowed") >= 0);
+ }
+ close();
+ }
+
+ @Entity
+ static class PersistentInnerClassEntity1 {
+
+ @PrimaryKey
+ private int key;
+
+ private PersistentInnerClass f;
+ }
+
+ /* An inner (non-static) class is illegal. */
+ @Persistent
+ class PersistentInnerClass {
+
+ private int x;
+ }
+
+ /**
+ * Disallow @Entity inner class.
+ */
+ public void testEntityInnerClass()
+ throws DatabaseException {
+
+ open();
+ try {
+ store.getPrimaryIndex(Integer.class,
+ EntityInnerClassEntity.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().indexOf
+ ("Inner classes not allowed") >= 0);
+ }
+ close();
+ }
+
+ /* An inner (non-static) class is illegal. */
+ @Entity
+ class EntityInnerClassEntity {
+
+ @PrimaryKey
+ private int key;
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/OperationTest.java b/test/scr024/src/com/sleepycat/persist/test/OperationTest.java
new file mode 100644
index 00000000..4fc81fc2
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/OperationTest.java
@@ -0,0 +1,1552 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.DeleteAction.CASCADE;
+import static com.sleepycat.persist.model.DeleteAction.NULLIFY;
+import static com.sleepycat.persist.model.Relationship.MANY_TO_MANY;
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.Test;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.StatsConfig;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.persist.EntityCursor;
+import com.sleepycat.persist.EntityIndex;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.impl.Store;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.KeyField;
+import com.sleepycat.persist.model.NotPersistent;
+import com.sleepycat.persist.model.NotTransient;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.persist.raw.RawStore;
+import com.sleepycat.util.test.TxnTestCase;
+
+/**
+ * Tests misc store and index operations that are not tested by IndexTest.
+ *
+ * @author Mark Hayes
+ */
+public class OperationTest extends TxnTestCase {
+
+ private static final String STORE_NAME = "test";
+
+ static protected Class<?> testClass = OperationTest.class;
+
+ public static Test suite() {
+ return txnTestSuite(testClass, null, null);
+ }
+
+ private EntityStore store;
+
+ private void openReadOnly()
+ throws DatabaseException {
+
+ StoreConfig config = new StoreConfig();
+ config.setReadOnly(true);
+ open(config);
+ }
+
+ private void open()
+ throws DatabaseException {
+
+ open((Class) null);
+ }
+
+ private void open(Class clsToRegister)
+ throws DatabaseException {
+
+ StoreConfig config = new StoreConfig();
+ config.setAllowCreate(envConfig.getAllowCreate());
+ if (clsToRegister != null) {
+ com.sleepycat.persist.model.EntityModel model =
+ new com.sleepycat.persist.model.AnnotationModel();
+ model.registerClass(clsToRegister);
+ config.setModel(model);
+ }
+ open(config);
+ }
+
+ private void open(StoreConfig config)
+ throws DatabaseException {
+
+ config.setTransactional(envConfig.getTransactional());
+ store = new EntityStore(env, STORE_NAME, config);
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ store = null;
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ super.setUp();
+ }
+
+ /**
+ * The store must be closed before closing the environment.
+ */
+ @Override
+ public void tearDown()
+ throws Exception {
+
+ try {
+ if (store != null) {
+ store.close();
+ }
+ } catch (Throwable e) {
+ System.out.println("During tearDown: " + e);
+ }
+ store = null;
+ super.tearDown();
+ }
+
+ public void testReadOnly()
+ throws DatabaseException {
+
+ open();
+ PrimaryIndex<Integer,SharedSequenceEntity1> priIndex =
+ store.getPrimaryIndex(Integer.class, SharedSequenceEntity1.class);
+ Transaction txn = txnBegin();
+ SharedSequenceEntity1 e = new SharedSequenceEntity1();
+ priIndex.put(txn, e);
+ assertEquals(1, e.key);
+ txnCommit(txn);
+ close();
+
+ /*
+ * Check that we can open the store read-only and read the records
+ * written above.
+ */
+ openReadOnly();
+ priIndex =
+ store.getPrimaryIndex(Integer.class, SharedSequenceEntity1.class);
+ e = priIndex.get(1);
+ assertNotNull(e);
+ close();
+ }
+
+
+
+ public void testUninitializedCursor()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Integer,MyEntity> priIndex =
+ store.getPrimaryIndex(Integer.class, MyEntity.class);
+
+ Transaction txn = txnBeginCursor();
+
+ MyEntity e = new MyEntity();
+ e.priKey = 1;
+ e.secKey = 1;
+ priIndex.put(txn, e);
+
+ EntityCursor<MyEntity> entities = priIndex.entities(txn, null);
+ try {
+ entities.nextDup();
+ fail();
+ } catch (IllegalStateException expected) {}
+ try {
+ entities.prevDup();
+ fail();
+ } catch (IllegalStateException expected) {}
+ try {
+ entities.current();
+ fail();
+ } catch (IllegalStateException expected) {}
+ try {
+ entities.delete();
+ fail();
+ } catch (IllegalStateException expected) {}
+ try {
+ entities.update(e);
+ fail();
+ } catch (IllegalStateException expected) {}
+ try {
+ entities.count();
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ entities.close();
+ txnCommit(txn);
+ close();
+ }
+
+ public void testCursorCount()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Integer,MyEntity> priIndex =
+ store.getPrimaryIndex(Integer.class, MyEntity.class);
+
+ SecondaryIndex<Integer,Integer,MyEntity> secIndex =
+ store.getSecondaryIndex(priIndex, Integer.class, "secKey");
+
+ Transaction txn = txnBeginCursor();
+
+ MyEntity e = new MyEntity();
+ e.priKey = 1;
+ e.secKey = 1;
+ priIndex.put(txn, e);
+
+ EntityCursor<MyEntity> cursor = secIndex.entities(txn, null);
+ cursor.next();
+ assertEquals(1, cursor.count());
+ cursor.close();
+
+ e.priKey = 2;
+ priIndex.put(txn, e);
+ cursor = secIndex.entities(txn, null);
+ cursor.next();
+ assertEquals(2, cursor.count());
+ cursor.close();
+
+ txnCommit(txn);
+ close();
+ }
+
+ public void testCursorUpdate()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Integer,MyEntity> priIndex =
+ store.getPrimaryIndex(Integer.class, MyEntity.class);
+
+ SecondaryIndex<Integer,Integer,MyEntity> secIndex =
+ store.getSecondaryIndex(priIndex, Integer.class, "secKey");
+
+ Transaction txn = txnBeginCursor();
+
+ Integer k;
+ MyEntity e = new MyEntity();
+ e.priKey = 1;
+ e.secKey = 2;
+ priIndex.put(txn, e);
+
+ /* update() with primary entity cursor. */
+ EntityCursor<MyEntity> entities = priIndex.entities(txn, null);
+ e = entities.next();
+ assertNotNull(e);
+ assertEquals(1, e.priKey);
+ assertEquals(Integer.valueOf(2), e.secKey);
+ e.secKey = null;
+ assertTrue(entities.update(e));
+ e = entities.current();
+ assertNotNull(e);
+ assertEquals(1, e.priKey);
+ assertEquals(null, e.secKey);
+ e.secKey = 3;
+ assertTrue(entities.update(e));
+ e = entities.current();
+ assertNotNull(e);
+ assertEquals(1, e.priKey);
+ assertEquals(Integer.valueOf(3), e.secKey);
+ entities.close();
+
+ /* update() with primary keys cursor. */
+ EntityCursor<Integer> keys = priIndex.keys(txn, null);
+ k = keys.next();
+ assertNotNull(k);
+ assertEquals(Integer.valueOf(1), k);
+ try {
+ keys.update(2);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ }
+ keys.close();
+
+ /* update() with secondary entity cursor. */
+ entities = secIndex.entities(txn, null);
+ e = entities.next();
+ assertNotNull(e);
+ assertEquals(1, e.priKey);
+ assertEquals(Integer.valueOf(3), e.secKey);
+ try {
+ entities.update(e);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ } catch (IllegalArgumentException expectedForDbCore) {
+ }
+ entities.close();
+
+ /* update() with secondary keys cursor. */
+ keys = secIndex.keys(txn, null);
+ k = keys.next();
+ assertNotNull(k);
+ assertEquals(Integer.valueOf(3), k);
+ try {
+ keys.update(k);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ }
+ keys.close();
+
+ txnCommit(txn);
+ close();
+ }
+
+ public void testCursorDelete()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Integer,MyEntity> priIndex =
+ store.getPrimaryIndex(Integer.class, MyEntity.class);
+
+ SecondaryIndex<Integer,Integer,MyEntity> secIndex =
+ store.getSecondaryIndex(priIndex, Integer.class, "secKey");
+
+ Transaction txn = txnBeginCursor();
+
+ /* delete() with primary and secondary entities cursor. */
+
+ for (EntityIndex index : new EntityIndex[] { priIndex, secIndex }) {
+
+ MyEntity e = new MyEntity();
+ e.priKey = 1;
+ e.secKey = 1;
+ priIndex.put(txn, e);
+ e.priKey = 2;
+ priIndex.put(txn, e);
+
+ EntityCursor<MyEntity> cursor = index.entities(txn, null);
+
+ e = cursor.next();
+ assertNotNull(e);
+ assertEquals(1, e.priKey);
+ e = cursor.current();
+ assertNotNull(e);
+ assertEquals(1, e.priKey);
+ assertTrue(cursor.delete());
+ assertTrue(!cursor.delete());
+ assertNull(cursor.current());
+
+ e = cursor.next();
+ assertNotNull(e);
+ assertEquals(2, e.priKey);
+ e = cursor.current();
+ assertNotNull(e);
+ assertEquals(2, e.priKey);
+ assertTrue(cursor.delete());
+ assertTrue(!cursor.delete());
+ assertNull(cursor.current());
+
+ e = cursor.next();
+ assertNull(e);
+
+ if (index == priIndex) {
+ e = new MyEntity();
+ e.priKey = 2;
+ e.secKey = 1;
+ assertTrue(!cursor.update(e));
+ }
+
+ cursor.close();
+ }
+
+ /* delete() with primary and secondary keys cursor. */
+
+ for (EntityIndex index : new EntityIndex[] { priIndex, secIndex }) {
+
+ MyEntity e = new MyEntity();
+ e.priKey = 1;
+ e.secKey = 1;
+ priIndex.put(txn, e);
+ e.priKey = 2;
+ priIndex.put(txn, e);
+
+ EntityCursor<Integer> cursor = index.keys(txn, null);
+
+ Integer k = cursor.next();
+ assertNotNull(k);
+ assertEquals(1, k.intValue());
+ k = cursor.current();
+ assertNotNull(k);
+ assertEquals(1, k.intValue());
+ assertTrue(cursor.delete());
+ assertTrue(!cursor.delete());
+ assertNull(cursor.current());
+
+ int expectKey = (index == priIndex) ? 2 : 1;
+ k = cursor.next();
+ assertNotNull(k);
+ assertEquals(expectKey, k.intValue());
+ k = cursor.current();
+ assertNotNull(k);
+ assertEquals(expectKey, k.intValue());
+ assertTrue(cursor.delete());
+ assertTrue(!cursor.delete());
+ assertNull(cursor.current());
+
+ k = cursor.next();
+ assertNull(k);
+
+ cursor.close();
+ }
+
+ txnCommit(txn);
+ close();
+ }
+
+ public void testDeleteFromSubIndex()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Integer,MyEntity> priIndex =
+ store.getPrimaryIndex(Integer.class, MyEntity.class);
+
+ SecondaryIndex<Integer,Integer,MyEntity> secIndex =
+ store.getSecondaryIndex(priIndex, Integer.class, "secKey");
+
+ Transaction txn = txnBegin();
+ MyEntity e = new MyEntity();
+ e.secKey = 1;
+ e.priKey = 1;
+ priIndex.put(txn, e);
+ e.priKey = 2;
+ priIndex.put(txn, e);
+ e.priKey = 3;
+ priIndex.put(txn, e);
+ e.priKey = 4;
+ priIndex.put(txn, e);
+ txnCommit(txn);
+
+ EntityIndex<Integer,MyEntity> subIndex = secIndex.subIndex(1);
+ txn = txnBeginCursor();
+ e = subIndex.get(txn, 1, null);
+ assertEquals(1, e.priKey);
+ assertEquals(Integer.valueOf(1), e.secKey);
+ e = subIndex.get(txn, 2, null);
+ assertEquals(2, e.priKey);
+ assertEquals(Integer.valueOf(1), e.secKey);
+ e = subIndex.get(txn, 3, null);
+ assertEquals(3, e.priKey);
+ assertEquals(Integer.valueOf(1), e.secKey);
+ e = subIndex.get(txn, 5, null);
+ assertNull(e);
+
+ boolean deleted = subIndex.delete(txn, 1);
+ assertTrue(deleted);
+ assertNull(subIndex.get(txn, 1, null));
+ assertNotNull(subIndex.get(txn, 2, null));
+
+ EntityCursor<MyEntity> cursor = subIndex.entities(txn, null);
+ boolean saw4 = false;
+ for (MyEntity e2 = cursor.first(); e2 != null; e2 = cursor.next()) {
+ if (e2.priKey == 3) {
+ cursor.delete();
+ }
+ if (e2.priKey == 4) {
+ saw4 = true;
+ }
+ }
+ cursor.close();
+ assertTrue(saw4);
+ assertNull(subIndex.get(txn, 1, null));
+ assertNull(subIndex.get(txn, 3, null));
+ assertNotNull(subIndex.get(txn, 2, null));
+ assertNotNull(subIndex.get(txn, 4, null));
+
+ txnCommit(txn);
+ close();
+ }
+
+ @Entity
+ static class MyEntity {
+
+ @PrimaryKey
+ private int priKey;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private Integer secKey;
+
+ private MyEntity() {}
+ }
+
+ public void testSharedSequence()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Integer,SharedSequenceEntity1> priIndex1 =
+ store.getPrimaryIndex(Integer.class, SharedSequenceEntity1.class);
+
+ PrimaryIndex<Integer,SharedSequenceEntity2> priIndex2 =
+ store.getPrimaryIndex(Integer.class, SharedSequenceEntity2.class);
+
+ Transaction txn = txnBegin();
+ SharedSequenceEntity1 e1 = new SharedSequenceEntity1();
+ SharedSequenceEntity2 e2 = new SharedSequenceEntity2();
+ priIndex1.put(txn, e1);
+ assertEquals(1, e1.key);
+ priIndex2.putNoOverwrite(txn, e2);
+ assertEquals(Integer.valueOf(2), e2.key);
+ e1.key = 0;
+ priIndex1.putNoOverwrite(txn, e1);
+ assertEquals(3, e1.key);
+ e2.key = null;
+ priIndex2.put(txn, e2);
+ assertEquals(Integer.valueOf(4), e2.key);
+ txnCommit(txn);
+
+ close();
+ }
+
+ @Entity
+ static class SharedSequenceEntity1 {
+
+ @PrimaryKey(sequence="shared")
+ private int key;
+ }
+
+ @Entity
+ static class SharedSequenceEntity2 {
+
+ @PrimaryKey(sequence="shared")
+ private Integer key;
+ }
+
+ public void testSeparateSequence()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Integer,SeparateSequenceEntity1> priIndex1 =
+ store.getPrimaryIndex
+ (Integer.class, SeparateSequenceEntity1.class);
+
+ PrimaryIndex<Integer,SeparateSequenceEntity2> priIndex2 =
+ store.getPrimaryIndex
+ (Integer.class, SeparateSequenceEntity2.class);
+
+ Transaction txn = txnBegin();
+ SeparateSequenceEntity1 e1 = new SeparateSequenceEntity1();
+ SeparateSequenceEntity2 e2 = new SeparateSequenceEntity2();
+ priIndex1.put(txn, e1);
+ assertEquals(1, e1.key);
+ priIndex2.putNoOverwrite(txn, e2);
+ assertEquals(Integer.valueOf(1), e2.key);
+ e1.key = 0;
+ priIndex1.putNoOverwrite(txn, e1);
+ assertEquals(2, e1.key);
+ e2.key = null;
+ priIndex2.put(txn, e2);
+ assertEquals(Integer.valueOf(2), e2.key);
+ txnCommit(txn);
+
+ close();
+ }
+
+ @Entity
+ static class SeparateSequenceEntity1 {
+
+ @PrimaryKey(sequence="seq1")
+ private int key;
+ }
+
+ @Entity
+ static class SeparateSequenceEntity2 {
+
+ @PrimaryKey(sequence="seq2")
+ private Integer key;
+ }
+
+ public void testCompositeSequence()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<CompositeSequenceEntity1.Key,CompositeSequenceEntity1>
+ priIndex1 =
+ store.getPrimaryIndex
+ (CompositeSequenceEntity1.Key.class,
+ CompositeSequenceEntity1.class);
+
+ PrimaryIndex<CompositeSequenceEntity2.Key,CompositeSequenceEntity2>
+ priIndex2 =
+ store.getPrimaryIndex
+ (CompositeSequenceEntity2.Key.class,
+ CompositeSequenceEntity2.class);
+
+ Transaction txn = txnBegin();
+ CompositeSequenceEntity1 e1 = new CompositeSequenceEntity1();
+ CompositeSequenceEntity2 e2 = new CompositeSequenceEntity2();
+ priIndex1.put(txn, e1);
+ assertEquals(1, e1.key.key);
+ priIndex2.putNoOverwrite(txn, e2);
+ assertEquals(Integer.valueOf(1), e2.key.key);
+ e1.key = null;
+ priIndex1.putNoOverwrite(txn, e1);
+ assertEquals(2, e1.key.key);
+ e2.key = null;
+ priIndex2.put(txn, e2);
+ assertEquals(Integer.valueOf(2), e2.key.key);
+ txnCommit(txn);
+
+ EntityCursor<CompositeSequenceEntity1> c1 = priIndex1.entities();
+ e1 = c1.next();
+ assertEquals(2, e1.key.key);
+ e1 = c1.next();
+ assertEquals(1, e1.key.key);
+ e1 = c1.next();
+ assertNull(e1);
+ c1.close();
+
+ EntityCursor<CompositeSequenceEntity2> c2 = priIndex2.entities();
+ e2 = c2.next();
+ assertEquals(Integer.valueOf(2), e2.key.key);
+ e2 = c2.next();
+ assertEquals(Integer.valueOf(1), e2.key.key);
+ e2 = c2.next();
+ assertNull(e2);
+ c2.close();
+
+ close();
+ }
+
+ @Entity
+ static class CompositeSequenceEntity1 {
+
+ @Persistent
+ static class Key implements Comparable<Key> {
+
+ @KeyField(1)
+ private int key;
+
+ public int compareTo(Key o) {
+ /* Reverse the natural order. */
+ return o.key - key;
+ }
+ }
+
+ @PrimaryKey(sequence="seq1")
+ private Key key;
+ }
+
+ /**
+ * Same as CompositeSequenceEntity1 but using Integer rather than int for
+ * the key type.
+ */
+ @Entity
+ static class CompositeSequenceEntity2 {
+
+ @Persistent
+ static class Key implements Comparable<Key> {
+
+ @KeyField(1)
+ private Integer key;
+
+ public int compareTo(Key o) {
+ /* Reverse the natural order. */
+ return o.key - key;
+ }
+ }
+
+ @PrimaryKey(sequence="seq2")
+ private Key key;
+ }
+
+ /**
+ * When opening read-only, secondaries are not opened when the primary is
+ * opened, causing a different code path to be used for opening
+ * secondaries. For a RawStore in particular, this caused an unreported
+ * NullPointerException in JE 3.0.12. No SR was created because the use
+ * case is very obscure and was discovered by code inspection.
+ */
+ public void testOpenRawStoreReadOnly()
+ throws DatabaseException {
+
+ open();
+ store.getPrimaryIndex(Integer.class, MyEntity.class);
+ close();
+
+ StoreConfig config = new StoreConfig();
+ config.setReadOnly(true);
+ config.setTransactional(envConfig.getTransactional());
+ RawStore rawStore = new RawStore(env, "test", config);
+
+ String clsName = MyEntity.class.getName();
+ rawStore.getSecondaryIndex(clsName, "secKey");
+
+ rawStore.close();
+ }
+
+ /**
+ * When opening an X_TO_MANY secondary that has a persistent key class, the
+ * key class was not recognized as being persistent if it was never before
+ * referenced when getSecondaryIndex was called. This was a bug in JE
+ * 3.0.12, reported on OTN. [#15103]
+ */
+ public void testToManyKeyClass()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Integer,ToManyKeyEntity> priIndex =
+ store.getPrimaryIndex(Integer.class, ToManyKeyEntity.class);
+ SecondaryIndex<ToManyKey,Integer,ToManyKeyEntity> secIndex =
+ store.getSecondaryIndex(priIndex, ToManyKey.class, "key2");
+
+ priIndex.put(new ToManyKeyEntity());
+ secIndex.get(new ToManyKey());
+
+ close();
+ }
+
+ /**
+ * Test a fix for a bug where opening a TO_MANY secondary index would fail
+ * fail with "IllegalArgumentException: Wrong secondary key class: ..."
+ * when the store was opened read-only. [#15156]
+ */
+ public void testToManyReadOnly()
+ throws DatabaseException {
+
+ open();
+ PrimaryIndex<Integer,ToManyKeyEntity> priIndex =
+ store.getPrimaryIndex(Integer.class, ToManyKeyEntity.class);
+ priIndex.put(new ToManyKeyEntity());
+ close();
+
+ openReadOnly();
+ priIndex = store.getPrimaryIndex(Integer.class, ToManyKeyEntity.class);
+ SecondaryIndex<ToManyKey,Integer,ToManyKeyEntity> secIndex =
+ store.getSecondaryIndex(priIndex, ToManyKey.class, "key2");
+ secIndex.get(new ToManyKey());
+ close();
+ }
+
+ @Persistent
+ static class ToManyKey {
+
+ @KeyField(1)
+ int value = 99;
+ }
+
+ @Entity
+ static class ToManyKeyEntity {
+
+ @PrimaryKey
+ int key = 88;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ Set<ToManyKey> key2;
+
+ ToManyKeyEntity() {
+ key2 = new HashSet<ToManyKey>();
+ key2.add(new ToManyKey());
+ }
+ }
+
+
+
+ /**
+ * When Y is opened and X has a key with relatedEntity=Y.class, X should
+ * be opened automatically. If X is not opened, foreign key constraints
+ * will not be enforced. [#15358]
+ */
+ public void testAutoOpenRelatedEntity()
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,RelatedY> priY;
+ PrimaryIndex<Integer,RelatedX> priX;
+
+ /* Opening X should create (and open) Y and enforce constraints. */
+ open();
+ priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
+ PersistTestUtils.assertDbExists
+ (true, env, STORE_NAME, RelatedY.class.getName(), null);
+ if (isTransactional) {
+ /* Constraint enforcement requires transactions. */
+ try {
+ priX.put(new RelatedX());
+ fail();
+ } catch (DatabaseException e) {
+ assertTrue
+ ("" + e.getMessage(), (e.getMessage().indexOf
+ ("foreign key not allowed: it is not present") >= 0) ||
+ (e.getMessage().indexOf("DB_FOREIGN_CONFLICT") >= 0));
+ }
+ }
+ priY = store.getPrimaryIndex(Integer.class, RelatedY.class);
+ priY.put(new RelatedY());
+ priX.put(new RelatedX());
+ close();
+
+ /* Delete should cascade even when X is not opened explicitly. */
+ open();
+ priY = store.getPrimaryIndex(Integer.class, RelatedY.class);
+ assertEquals(1, priY.count());
+ priY.delete(88);
+ assertEquals(0, priY.count());
+ priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
+ assertEquals(0, priX.count()); /* Failed prior to [#15358] fix. */
+ close();
+ }
+
+ @Entity
+ static class RelatedX {
+
+ @PrimaryKey
+ int key = 99;
+
+ @SecondaryKey(relate=ONE_TO_ONE,
+ relatedEntity=RelatedY.class,
+ onRelatedEntityDelete=CASCADE)
+ int key2 = 88;
+
+ RelatedX() {
+ }
+ }
+
+ @Entity
+ static class RelatedY {
+
+ @PrimaryKey
+ int key = 88;
+
+ RelatedY() {
+ }
+ }
+
+ public void testSecondaryBulkLoad1()
+ throws DatabaseException {
+
+ doSecondaryBulkLoad(true);
+ }
+
+ public void testSecondaryBulkLoad2()
+ throws DatabaseException {
+
+ doSecondaryBulkLoad(false);
+ }
+
+ private void doSecondaryBulkLoad(boolean closeAndOpenNormally)
+ throws DatabaseException {
+
+ PrimaryIndex<Integer,RelatedX> priX;
+ PrimaryIndex<Integer,RelatedY> priY;
+ SecondaryIndex<Integer,Integer,RelatedX> secX;
+
+ /* Open priX with SecondaryBulkLoad=true. */
+ StoreConfig config = new StoreConfig();
+ config.setAllowCreate(true);
+ config.setSecondaryBulkLoad(true);
+ open(config);
+
+ /* Getting priX should not create the secondary index. */
+ priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
+ PersistTestUtils.assertDbExists
+ (false, env, STORE_NAME, RelatedX.class.getName(), "key2");
+
+ /* We can put records that violate the secondary key constraint. */
+ priX.put(new RelatedX());
+
+ if (closeAndOpenNormally) {
+ /* Open normally and attempt to populate the secondary. */
+ close();
+ open();
+ if (isTransactional && DbCompat.POPULATE_ENFORCES_CONSTRAINTS) {
+ /* Constraint enforcement requires transactions. */
+ try {
+ /* Before adding the foreign key, constraint is violated. */
+ priX = store.getPrimaryIndex(Integer.class,
+ RelatedX.class);
+ fail();
+ } catch (DatabaseException e) {
+ assertTrue
+ (e.toString(),
+ e.toString().contains("foreign key not allowed"));
+ }
+ }
+ /* Open priX with SecondaryBulkLoad=true. */
+ close();
+ open(config);
+ /* Add the foreign key to avoid the constraint error. */
+ priY = store.getPrimaryIndex(Integer.class, RelatedY.class);
+ priY.put(new RelatedY());
+ /* Open normally and the secondary will be populated. */
+ close();
+ open();
+ priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
+ PersistTestUtils.assertDbExists
+ (true, env, STORE_NAME, RelatedX.class.getName(), "key2");
+ secX = store.getSecondaryIndex(priX, Integer.class, "key2");
+ } else {
+ /* Get secondary index explicitly and it will be populated. */
+ if (isTransactional && DbCompat.POPULATE_ENFORCES_CONSTRAINTS) {
+ /* Constraint enforcement requires transactions. */
+ try {
+ /* Before adding the foreign key, constraint is violated. */
+ secX = store.getSecondaryIndex(priX, Integer.class,
+ "key2");
+ fail();
+ } catch (DatabaseException e) {
+ assertTrue
+ (e.toString(),
+ e.toString().contains("foreign key not allowed"));
+ }
+ }
+ /* Add the foreign key. */
+ priY = store.getPrimaryIndex(Integer.class, RelatedY.class);
+ priY.put(new RelatedY());
+ secX = store.getSecondaryIndex(priX, Integer.class, "key2");
+ PersistTestUtils.assertDbExists
+ (true, env, STORE_NAME, RelatedX.class.getName(), "key2");
+ }
+
+ RelatedX x = secX.get(88);
+ assertNotNull(x);
+ close();
+ }
+
+ public void testPersistentFields()
+ throws DatabaseException {
+
+ open();
+ PrimaryIndex<Integer, PersistentFields> pri =
+ store.getPrimaryIndex(Integer.class, PersistentFields.class);
+ PersistentFields o1 = new PersistentFields(-1, 1, 2, 3, 4, 5, 6);
+ assertNull(pri.put(o1));
+ PersistentFields o2 = pri.get(-1);
+ assertNotNull(o2);
+ assertEquals(0, o2.transient1);
+ assertEquals(0, o2.transient2);
+ assertEquals(0, o2.transient3);
+ assertEquals(4, o2.persistent1);
+ assertEquals(5, o2.persistent2);
+ assertEquals(6, o2.persistent3);
+ close();
+ }
+
+ @Entity
+ static class PersistentFields {
+
+ @PrimaryKey int key;
+
+ transient int transient1;
+ @NotPersistent int transient2;
+ @NotPersistent transient int transient3;
+
+ int persistent1;
+ @NotTransient int persistent2;
+ @NotTransient transient int persistent3;
+
+ PersistentFields(int k,
+ int t1,
+ int t2,
+ int t3,
+ int p1,
+ int p2,
+ int p3) {
+ key = k;
+ transient1 = t1;
+ transient2 = t2;
+ transient3 = t3;
+ persistent1 = p1;
+ persistent2 = p2;
+ persistent3 = p3;
+ }
+
+ private PersistentFields() {}
+ }
+
+ /**
+ * When a primary or secondary has a persistent key class, the key class
+ * was not recognized as being persistent when getPrimaryConfig,
+ * getSecondaryConfig, or getSubclassIndex was called, if that key class
+ * was not previously referenced. All three cases are tested by calling
+ * getSecondaryConfig. This was a bug in JE 3.3.69, reported on OTN.
+ * [#16407]
+ */
+ public void testKeyClassInitialization()
+ throws DatabaseException {
+
+ open();
+ store.getSecondaryConfig(ToManyKeyEntity.class, "key2");
+ close();
+ }
+
+ public void testKeyName()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Long, BookEntity> pri1 =
+ store.getPrimaryIndex(Long.class, BookEntity.class);
+ PrimaryIndex<Long, AuthorEntity> pri2 =
+ store.getPrimaryIndex(Long.class, AuthorEntity.class);
+
+ BookEntity book = new BookEntity();
+ pri1.put(book);
+ AuthorEntity author = new AuthorEntity();
+ author.bookIds.add(book.bookId);
+ pri2.put(author);
+
+ close();
+
+ open();
+ pri1 = store.getPrimaryIndex(Long.class, BookEntity.class);
+ pri2 = store.getPrimaryIndex(Long.class, AuthorEntity.class);
+ book = pri1.get(1L);
+ assertNotNull(book);
+ author = pri2.get(1L);
+ assertNotNull(author);
+ close();
+ }
+
+ @Entity
+ static class AuthorEntity {
+
+ @PrimaryKey(sequence="authorSeq")
+ long authorId;
+
+ @SecondaryKey(relate=MANY_TO_MANY, relatedEntity=BookEntity.class,
+ name="bookId", onRelatedEntityDelete=NULLIFY)
+ Set<Long> bookIds = new HashSet<Long>();
+ }
+
+ @Entity
+ static class BookEntity {
+
+ @PrimaryKey(sequence="bookSeq")
+ long bookId;
+ }
+
+ /**
+ * Checks that we get an appropriate exception when storing an entity
+ * subclass instance, which contains a secondary key, without registering
+ * the subclass up front. [#16399]
+ */
+ public void testPutEntitySubclassWithoutRegisterClass()
+ throws DatabaseException {
+
+ open();
+
+ final PrimaryIndex<Long, Statement> pri =
+ store.getPrimaryIndex(Long.class, Statement.class);
+
+ final Transaction txn = txnBegin();
+ pri.put(txn, new Statement(1));
+ try {
+ pri.put(txn, new ExtendedStatement(2, null));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.toString(), expected.getMessage().contains
+ ("Entity subclasses defining a secondary key must be " +
+ "registered by calling EntityModel.registerClass or " +
+ "EntityStore.getSubclassIndex before storing an instance " +
+ "of the subclass: " + ExtendedStatement.class.getName()));
+ }
+ txnAbort(txn);
+
+ close();
+ }
+
+ /**
+ * Checks that registerClass avoids an exception when storing an entity
+ * subclass instance, which defines a secondary key. [#16399]
+ */
+ public void testPutEntitySubclassWithRegisterClass()
+ throws DatabaseException {
+
+ open(ExtendedStatement.class);
+
+ final PrimaryIndex<Long, Statement> pri =
+ store.getPrimaryIndex(Long.class, Statement.class);
+
+ final Transaction txn = txnBegin();
+ pri.put(txn, new Statement(1));
+ pri.put(txn, new ExtendedStatement(2, "abc"));
+ txnCommit(txn);
+
+ final SecondaryIndex<String, Long, ExtendedStatement> sec =
+ store.getSubclassIndex(pri, ExtendedStatement.class,
+ String.class, "name");
+
+ ExtendedStatement o = sec.get("abc");
+ assertNotNull(o);
+ assertEquals(2, o.id);
+
+ close();
+ }
+
+ /**
+ * Same as testPutEntitySubclassWithRegisterClass but store the first
+ * instance of the subclass after closing and reopening the store,
+ * *without* calling registerClass. This ensures that a single call to
+ * registerClass is sufficient and subsequent use of the store does not
+ * require it. [#16399]
+ */
+ public void testPutEntitySubclassWithRegisterClass2()
+ throws DatabaseException {
+
+ open(ExtendedStatement.class);
+
+ PrimaryIndex<Long, Statement> pri =
+ store.getPrimaryIndex(Long.class, Statement.class);
+
+ Transaction txn = txnBegin();
+ pri.put(txn, new Statement(1));
+ txnCommit(txn);
+
+ close();
+ open();
+
+ pri = store.getPrimaryIndex(Long.class, Statement.class);
+
+ txn = txnBegin();
+ pri.put(txn, new ExtendedStatement(2, "abc"));
+ txnCommit(txn);
+
+ final SecondaryIndex<String, Long, ExtendedStatement> sec =
+ store.getSubclassIndex(pri, ExtendedStatement.class,
+ String.class, "name");
+
+ ExtendedStatement o = sec.get("abc");
+ assertNotNull(o);
+ assertEquals(2, o.id);
+
+ close();
+ }
+
+ /**
+ * Checks that getSubclassIndex can be used instead of registerClass to
+ * avoid an exception when storing an entity subclass instance, which
+ * defines a secondary key. [#16399]
+ */
+ public void testPutEntitySubclassWithGetSubclassIndex()
+ throws DatabaseException {
+
+ open();
+
+ final PrimaryIndex<Long, Statement> pri =
+ store.getPrimaryIndex(Long.class, Statement.class);
+
+ final SecondaryIndex<String, Long, ExtendedStatement> sec =
+ store.getSubclassIndex(pri, ExtendedStatement.class,
+ String.class, "name");
+
+ final Transaction txn = txnBegin();
+ pri.put(txn, new Statement(1));
+ pri.put(txn, new ExtendedStatement(2, "abc"));
+ txnCommit(txn);
+
+ ExtendedStatement o = sec.get("abc");
+ assertNotNull(o);
+ assertEquals(2, o.id);
+
+ close();
+ }
+
+ /**
+ * Same as testPutEntitySubclassWithGetSubclassIndex2 but store the first
+ * instance of the subclass after closing and reopening the store,
+ * *without* calling getSubclassIndex. This ensures that a single call to
+ * getSubclassIndex is sufficient and subsequent use of the store does not
+ * require it. [#16399]
+ */
+ public void testPutEntitySubclassWithGetSubclassIndex2()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<Long, Statement> pri =
+ store.getPrimaryIndex(Long.class, Statement.class);
+
+ SecondaryIndex<String, Long, ExtendedStatement> sec =
+ store.getSubclassIndex(pri, ExtendedStatement.class,
+ String.class, "name");
+
+ Transaction txn = txnBegin();
+ pri.put(txn, new Statement(1));
+ txnCommit(txn);
+
+ close();
+ open();
+
+ pri = store.getPrimaryIndex(Long.class, Statement.class);
+
+ txn = txnBegin();
+ pri.put(txn, new ExtendedStatement(2, "abc"));
+ txnCommit(txn);
+
+ sec = store.getSubclassIndex(pri, ExtendedStatement.class,
+ String.class, "name");
+
+ ExtendedStatement o = sec.get("abc");
+ assertNotNull(o);
+ assertEquals(2, o.id);
+
+ close();
+ }
+
+ /**
+ * Checks that secondary population occurs only once when an index is
+ * created, not every time it is opened, even when it is empty. This is a
+ * JE-only test because we don't have a portable way to get stats that
+ * indicate whether primary reads were performed. [#16399]
+ */
+
+ @Entity
+ static class Statement {
+
+ @PrimaryKey
+ long id;
+
+ Statement(long id) {
+ this.id = id;
+ }
+
+ private Statement() {}
+ }
+
+ @Persistent
+ static class ExtendedStatement extends Statement {
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ String name;
+
+ ExtendedStatement(long id, String name) {
+ super(id);
+ this.name = name;
+ }
+
+ private ExtendedStatement() {}
+ }
+
+ public void testCustomCompare()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<ReverseIntKey, CustomCompareEntity>
+ priIndex = store.getPrimaryIndex
+ (ReverseIntKey.class, CustomCompareEntity.class);
+
+ SecondaryIndex<ReverseIntKey, ReverseIntKey, CustomCompareEntity>
+ secIndex1 = store.getSecondaryIndex(priIndex, ReverseIntKey.class,
+ "secKey1");
+
+ SecondaryIndex<ReverseIntKey, ReverseIntKey, CustomCompareEntity>
+ secIndex2 = store.getSecondaryIndex(priIndex, ReverseIntKey.class,
+ "secKey2");
+
+ Transaction txn = txnBegin();
+ for (int i = 1; i <= 5; i += 1) {
+ assertTrue(priIndex.putNoOverwrite(txn,
+ new CustomCompareEntity(i)));
+ }
+ txnCommit(txn);
+
+ txn = txnBeginCursor();
+ EntityCursor<CustomCompareEntity> c = priIndex.entities(txn, null);
+ for (int i = 5; i >= 1; i -= 1) {
+ CustomCompareEntity e = c.next();
+ assertNotNull(e);
+ assertEquals(new ReverseIntKey(i), e.key);
+ }
+ c.close();
+ txnCommit(txn);
+
+ txn = txnBeginCursor();
+ c = secIndex1.entities(txn, null);
+ for (int i = -1; i >= -5; i -= 1) {
+ CustomCompareEntity e = c.next();
+ assertNotNull(e);
+ assertEquals(new ReverseIntKey(-i), e.key);
+ assertEquals(new ReverseIntKey(i), e.secKey1);
+ }
+ c.close();
+ txnCommit(txn);
+
+ txn = txnBeginCursor();
+ c = secIndex2.entities(txn, null);
+ for (int i = -1; i >= -5; i -= 1) {
+ CustomCompareEntity e = c.next();
+ assertNotNull(e);
+ assertEquals(new ReverseIntKey(-i), e.key);
+ assertTrue(e.secKey2.contains(new ReverseIntKey(i)));
+ }
+ c.close();
+ txnCommit(txn);
+
+ close();
+ }
+
+ @Entity
+ static class CustomCompareEntity {
+
+ @PrimaryKey
+ private ReverseIntKey key;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private ReverseIntKey secKey1;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ private Set<ReverseIntKey> secKey2 = new HashSet<ReverseIntKey>();
+
+ private CustomCompareEntity() {}
+
+ CustomCompareEntity(int i) {
+ key = new ReverseIntKey(i);
+ secKey1 = new ReverseIntKey(-i);
+ secKey2.add(new ReverseIntKey(-i));
+ }
+ }
+
+ @Persistent
+ static class ReverseIntKey implements Comparable<ReverseIntKey> {
+
+ @KeyField(1)
+ private int key;
+
+ public int compareTo(ReverseIntKey o) {
+ /* Reverse the natural order. */
+ return o.key - key;
+ }
+
+ private ReverseIntKey() {}
+
+ ReverseIntKey(int key) {
+ this.key = key;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return key == ((ReverseIntKey) o).key;
+ }
+
+ @Override
+ public int hashCode() {
+ return key;
+ }
+
+ @Override
+ public String toString() {
+ return "Key = " + key;
+ }
+ }
+
+ /**
+ * Ensures that custom comparators are persisted and work correctly during
+ * recovery. JE recovery uses comparators, so they are serialized and
+ * stored in the DatabaseImpl. They are deserialized during recovery prior
+ * to opening the EntityStore and its format catalog. But the formats are
+ * needed by the comparator, so they are specially created when needed.
+ *
+ * In particular we need to ensure that enum key fields work correctly,
+ * since their formats are not static (like simple type formats are).
+ * [#17140]
+ *
+ * Note that we don't need to actually cause a recovery in order to test
+ * the deserialization and subsequent use of comparators. The JE
+ * DatabaseConfig.setBtreeComparator method serializes and deserializes the
+ * comparator. The comparator is initialized on its first use, just as if
+ * recovery were run.
+ */
+ public void testStoredComparators()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<StoredComparatorEntity.Key,
+ StoredComparatorEntity> priIndex =
+ store.getPrimaryIndex(StoredComparatorEntity.Key.class,
+ StoredComparatorEntity.class);
+
+ SecondaryIndex<StoredComparatorEntity.MyEnum,
+ StoredComparatorEntity.Key,
+ StoredComparatorEntity> secIndex =
+ store.getSecondaryIndex
+ (priIndex, StoredComparatorEntity.MyEnum.class, "secKey");
+
+ final StoredComparatorEntity.Key[] priKeys =
+ new StoredComparatorEntity.Key[] {
+ new StoredComparatorEntity.Key
+ (StoredComparatorEntity.MyEnum.A, 1,
+ StoredComparatorEntity.MyEnum.A),
+ new StoredComparatorEntity.Key
+ (StoredComparatorEntity.MyEnum.A, 1,
+ StoredComparatorEntity.MyEnum.B),
+ new StoredComparatorEntity.Key
+ (StoredComparatorEntity.MyEnum.A, 2,
+ StoredComparatorEntity.MyEnum.A),
+ new StoredComparatorEntity.Key
+ (StoredComparatorEntity.MyEnum.A, 2,
+ StoredComparatorEntity.MyEnum.B),
+ new StoredComparatorEntity.Key
+ (StoredComparatorEntity.MyEnum.B, 1,
+ StoredComparatorEntity.MyEnum.A),
+ new StoredComparatorEntity.Key
+ (StoredComparatorEntity.MyEnum.B, 1,
+ StoredComparatorEntity.MyEnum.B),
+ new StoredComparatorEntity.Key
+ (StoredComparatorEntity.MyEnum.C, 0,
+ StoredComparatorEntity.MyEnum.C),
+ };
+
+ final StoredComparatorEntity.MyEnum[] secKeys =
+ new StoredComparatorEntity.MyEnum[] {
+ StoredComparatorEntity.MyEnum.C,
+ StoredComparatorEntity.MyEnum.B,
+ StoredComparatorEntity.MyEnum.A,
+ null,
+ StoredComparatorEntity.MyEnum.A,
+ StoredComparatorEntity.MyEnum.B,
+ StoredComparatorEntity.MyEnum.C,
+ };
+
+ assertEquals(priKeys.length, secKeys.length);
+ final int nEntities = priKeys.length;
+
+ Transaction txn = txnBegin();
+ for (int i = 0; i < nEntities; i += 1) {
+ priIndex.put(txn,
+ new StoredComparatorEntity(priKeys[i], secKeys[i]));
+ }
+ txnCommit(txn);
+
+ txn = txnBeginCursor();
+ EntityCursor<StoredComparatorEntity> entities =
+ priIndex.entities(txn, null);
+ for (int i = nEntities - 1; i >= 0; i -= 1) {
+ StoredComparatorEntity e = entities.next();
+ assertNotNull(e);
+ assertEquals(priKeys[i], e.key);
+ assertEquals(secKeys[i], e.secKey);
+ }
+ assertNull(entities.next());
+ entities.close();
+ txnCommit(txn);
+
+ txn = txnBeginCursor();
+ entities = secIndex.entities(txn, null);
+ for (StoredComparatorEntity.MyEnum myEnum :
+ EnumSet.allOf(StoredComparatorEntity.MyEnum.class)) {
+ for (int i = 0; i < nEntities; i += 1) {
+ if (secKeys[i] == myEnum) {
+ StoredComparatorEntity e = entities.next();
+ assertNotNull(e);
+ assertEquals(priKeys[i], e.key);
+ assertEquals(secKeys[i], e.secKey);
+ }
+ }
+ }
+ assertNull(entities.next());
+ entities.close();
+ txnCommit(txn);
+
+ close();
+ }
+
+ @Entity
+ static class StoredComparatorEntity {
+
+ enum MyEnum { A, B, C };
+
+ @Persistent
+ static class Key implements Comparable<Key> {
+
+ @KeyField(1)
+ MyEnum f1;
+
+ @KeyField(2)
+ Integer f2;
+
+ @KeyField(3)
+ MyEnum f3;
+
+ private Key() {}
+
+ Key(MyEnum f1, Integer f2, MyEnum f3) {
+ this.f1 = f1;
+ this.f2 = f2;
+ this.f3 = f3;
+ }
+
+ public int compareTo(Key o) {
+ /* Reverse the natural order. */
+ int i = f1.compareTo(o.f1);
+ if (i != 0) return -i;
+ i = f2.compareTo(o.f2);
+ if (i != 0) return -i;
+ i = f3.compareTo(o.f3);
+ if (i != 0) return -i;
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Key)) {
+ return false;
+ }
+ Key o = (Key) other;
+ return f1 == o.f1 &&
+ f2.equals(o.f2) &&
+ f3 == o.f3;
+ }
+
+ @Override
+ public int hashCode() {
+ return f1.ordinal() + f2 + f3.ordinal();
+ }
+
+ @Override
+ public String toString() {
+ return "[Key " + f1 + ' ' + f2 + ' ' + f3 + ']';
+ }
+ }
+
+ @PrimaryKey
+ Key key;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private MyEnum secKey;
+
+ private StoredComparatorEntity() {}
+
+ StoredComparatorEntity(Key key, MyEnum secKey) {
+ this.key = key;
+ this.secKey = secKey;
+ }
+
+ @Override
+ public String toString() {
+ return "[pri = " + key + " sec = " + secKey + ']';
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/PersistTestUtils.java b/test/scr024/src/com/sleepycat/persist/test/PersistTestUtils.java
new file mode 100644
index 00000000..354eb19f
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/PersistTestUtils.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.persist.test;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Environment;
+
+class PersistTestUtils {
+
+ /**
+ * Asserts than a database expectExists or does not exist. If keyName is
+ * null, checks an entity database. If keyName is non-null, checks a
+ * secondary database.
+ */
+ static void assertDbExists(boolean expectExists,
+ Environment env,
+ String storeName,
+ String entityClassName,
+ String keyName) {
+ String fileName;
+ String dbName;
+ if (DbCompat.SEPARATE_DATABASE_FILES) {
+ fileName = storeName + '-' + entityClassName;
+ if (keyName != null) {
+ fileName += "-" + keyName;
+ }
+ dbName = null;
+ } else {
+ fileName = null;
+ dbName = "persist#" + storeName + '#' + entityClassName;
+ if (keyName != null) {
+ dbName += "#" + keyName;
+ }
+ }
+ boolean exists = DbCompat.databaseExists(env, fileName, dbName);
+ if (expectExists != exists) {
+ TestCase.fail
+ ((expectExists ? "Does not exist: " : "Does exist: ") +
+ dbName);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/SequenceTest.java b/test/scr024/src/com/sleepycat/persist/test/SequenceTest.java
new file mode 100644
index 00000000..f2284260
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/SequenceTest.java
@@ -0,0 +1,469 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import java.io.File;
+
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.util.DualTestCase;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.KeyField;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class SequenceTest extends DualTestCase {
+
+ private File envHome;
+ private Environment env;
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ super.setUp();
+
+ envHome = new File(System.getProperty(SharedTestUtils.DEST_DIR));
+ SharedTestUtils.emptyDir(envHome);
+ }
+
+ @Override
+ public void tearDown()
+ throws Exception {
+
+ super.tearDown();
+
+ envHome = null;
+ env = null;
+ }
+
+ public void testSequenceKeys()
+ throws Exception {
+
+ Class[] classes = {
+ SequenceEntity_Long.class,
+ SequenceEntity_Integer.class,
+ SequenceEntity_Short.class,
+ SequenceEntity_Byte.class,
+ SequenceEntity_tlong.class,
+ SequenceEntity_tint.class,
+ SequenceEntity_tshort.class,
+ SequenceEntity_tbyte.class,
+ SequenceEntity_Long_composite.class,
+ SequenceEntity_Integer_composite.class,
+ SequenceEntity_Short_composite.class,
+ SequenceEntity_Byte_composite.class,
+ SequenceEntity_tlong_composite.class,
+ SequenceEntity_tint_composite.class,
+ SequenceEntity_tshort_composite.class,
+ SequenceEntity_tbyte_composite.class,
+ };
+
+ EnvironmentConfig envConfig = TestEnv.TXN.getConfig();
+ envConfig.setAllowCreate(true);
+ env = create(envHome, envConfig);
+
+ StoreConfig storeConfig = new StoreConfig();
+ storeConfig.setAllowCreate(true);
+ storeConfig.setTransactional(true);
+ EntityStore store = new EntityStore(env, "foo", storeConfig);
+
+ long seq = 0;
+
+ for (int i = 0; i < classes.length; i += 1) {
+ Class entityCls = classes[i];
+ SequenceEntity entity = (SequenceEntity) entityCls.newInstance();
+ Class keyCls = entity.getKeyClass();
+
+ PrimaryIndex<Object,SequenceEntity> index =
+ store.getPrimaryIndex(keyCls, entityCls);
+ index.putNoReturn(entity);
+ seq += 1;
+ assertEquals(seq, entity.getKey());
+
+ index.putNoReturn(entity);
+ assertEquals(seq, entity.getKey());
+
+ entity.nullifyKey();
+ index.putNoReturn(entity);
+ seq += 1;
+ assertEquals(seq, entity.getKey());
+ }
+
+ store.close();
+ close(env);
+ env = null;
+ }
+
+ interface SequenceEntity {
+ Class getKeyClass();
+ long getKey();
+ void nullifyKey();
+ }
+
+ @Entity
+ static class SequenceEntity_Long implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Long priKey;
+
+ public Class getKeyClass() {
+ return Long.class;
+ }
+
+ public long getKey() {
+ return priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_Integer implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Integer priKey;
+
+ public Class getKeyClass() {
+ return Integer.class;
+ }
+
+ public long getKey() {
+ return priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_Short implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Short priKey;
+
+ public Class getKeyClass() {
+ return Short.class;
+ }
+
+ public long getKey() {
+ return priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_Byte implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Byte priKey;
+
+ public Class getKeyClass() {
+ return Byte.class;
+ }
+
+ public long getKey() {
+ return priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_tlong implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ long priKey;
+
+ public Class getKeyClass() {
+ return Long.class;
+ }
+
+ public long getKey() {
+ return priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = 0;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_tint implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ int priKey;
+
+ public Class getKeyClass() {
+ return Integer.class;
+ }
+
+ public long getKey() {
+ return priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = 0;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_tshort implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ short priKey;
+
+ public Class getKeyClass() {
+ return Short.class;
+ }
+
+ public long getKey() {
+ return priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = 0;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_tbyte implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ byte priKey;
+
+ public Class getKeyClass() {
+ return Byte.class;
+ }
+
+ public long getKey() {
+ return priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = 0;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_Long_composite implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Key priKey;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ Long priKey;
+ }
+
+ public Class getKeyClass() {
+ return Key.class;
+ }
+
+ public long getKey() {
+ return priKey.priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_Integer_composite implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Key priKey;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ Integer priKey;
+ }
+
+ public Class getKeyClass() {
+ return Key.class;
+ }
+
+ public long getKey() {
+ return priKey.priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_Short_composite implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Key priKey;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ Short priKey;
+ }
+
+ public Class getKeyClass() {
+ return Key.class;
+ }
+
+ public long getKey() {
+ return priKey.priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_Byte_composite implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Key priKey;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ Byte priKey;
+ }
+
+ public Class getKeyClass() {
+ return Key.class;
+ }
+
+ public long getKey() {
+ return priKey.priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_tlong_composite implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Key priKey;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ long priKey;
+ }
+
+ public Class getKeyClass() {
+ return Key.class;
+ }
+
+ public long getKey() {
+ return priKey.priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_tint_composite implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Key priKey;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ int priKey;
+ }
+
+ public Class getKeyClass() {
+ return Key.class;
+ }
+
+ public long getKey() {
+ return priKey.priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_tshort_composite implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Key priKey;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ short priKey;
+ }
+
+ public Class getKeyClass() {
+ return Key.class;
+ }
+
+ public long getKey() {
+ return priKey.priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+
+ @Entity
+ static class SequenceEntity_tbyte_composite implements SequenceEntity {
+
+ @PrimaryKey(sequence="X")
+ Key priKey;
+
+ @Persistent
+ static class Key {
+ @KeyField(1)
+ byte priKey;
+ }
+
+ public Class getKeyClass() {
+ return Key.class;
+ }
+
+ public long getKey() {
+ return priKey.priKey;
+ }
+
+ public void nullifyKey() {
+ priKey = null;
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/persist/test/SubclassIndexTest.java b/test/scr024/src/com/sleepycat/persist/test/SubclassIndexTest.java
new file mode 100644
index 00000000..f1f5dd9e
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/persist/test/SubclassIndexTest.java
@@ -0,0 +1,251 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.persist.test;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+
+import java.io.File;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.db.util.DualTestCase;
+import com.sleepycat.persist.EntityCursor;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.AnnotationModel;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.EntityModel;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+public class SubclassIndexTest extends DualTestCase {
+
+ private File envHome;
+ private Environment env;
+ private EntityStore store;
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ super.setUp();
+
+ envHome = new File(System.getProperty(SharedTestUtils.DEST_DIR));
+ SharedTestUtils.emptyDir(envHome);
+ }
+
+ @Override
+ public void tearDown()
+ throws Exception {
+
+ super.tearDown();
+
+ envHome = null;
+ env = null;
+ }
+
+ private void open()
+ throws DatabaseException {
+
+ EnvironmentConfig envConfig = TestEnv.TXN.getConfig();
+ envConfig.setAllowCreate(true);
+ env = create(envHome, envConfig);
+
+ EntityModel model = new AnnotationModel();
+ model.registerClass(Manager.class);
+ model.registerClass(SalariedManager.class);
+
+ StoreConfig storeConfig = new StoreConfig();
+ storeConfig.setModel(model);
+ storeConfig.setAllowCreate(true);
+ storeConfig.setTransactional(true);
+ store = new EntityStore(env, "foo", storeConfig);
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ store = null;
+ close(env);
+ env = null;
+ }
+
+ public void testSubclassIndex()
+ throws DatabaseException {
+
+ open();
+
+ PrimaryIndex<String, Employee> employeesById =
+ store.getPrimaryIndex(String.class, Employee.class);
+
+ employeesById.put(new Employee("1"));
+ employeesById.put(new Manager("2", "a"));
+ employeesById.put(new Manager("3", "a"));
+ employeesById.put(new Manager("4", "b"));
+
+ Employee e;
+ Manager m;
+
+ e = employeesById.get("1");
+ assertNotNull(e);
+ assertTrue(!(e instanceof Manager));
+
+ /* Ensure DB exists BEFORE calling getSubclassIndex. [#15247] */
+ PersistTestUtils.assertDbExists
+ (true, env, "foo", Employee.class.getName(), "dept");
+
+ /* Normal use: Subclass index for a key in the subclass. */
+ SecondaryIndex<String, String, Manager> managersByDept =
+ store.getSubclassIndex
+ (employeesById, Manager.class, String.class, "dept");
+
+ m = managersByDept.get("a");
+ assertNotNull(m);
+ assertEquals("2", m.id);
+
+ m = managersByDept.get("b");
+ assertNotNull(m);
+ assertEquals("4", m.id);
+
+ Transaction txn = env.beginTransaction(null, null);
+ EntityCursor<Manager> managers = managersByDept.entities(txn, null);
+ try {
+ m = managers.next();
+ assertNotNull(m);
+ assertEquals("2", m.id);
+ m = managers.next();
+ assertNotNull(m);
+ assertEquals("3", m.id);
+ m = managers.next();
+ assertNotNull(m);
+ assertEquals("4", m.id);
+ m = managers.next();
+ assertNull(m);
+ } finally {
+ managers.close();
+ txn.commit();
+ }
+
+ /* Getting a subclass index for the entity class is also allowed. */
+ store.getSubclassIndex
+ (employeesById, Employee.class, String.class, "other");
+
+ /* Getting a subclass index for a base class key is not allowed. */
+ try {
+ store.getSubclassIndex
+ (employeesById, Manager.class, String.class, "other");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ close();
+ }
+
+ /**
+ * Previously this tested that a secondary key database was added only
+ * AFTER storing the first instance of the subclass that defines the key.
+ * Now that we require registering the subclass up front, the database is
+ * created up front also. So this test is somewhat less useful, but still
+ * nice to have around. [#16399]
+ */
+ public void testAddSecKey()
+ throws DatabaseException {
+
+ open();
+ PrimaryIndex<String, Employee> employeesById =
+ store.getPrimaryIndex(String.class, Employee.class);
+ employeesById.put(new Employee("1"));
+ assertTrue(hasEntityKey("dept"));
+ close();
+
+ open();
+ employeesById = store.getPrimaryIndex(String.class, Employee.class);
+ assertTrue(hasEntityKey("dept"));
+ employeesById.put(new Manager("2", "a"));
+ assertTrue(hasEntityKey("dept"));
+ close();
+
+ open();
+ assertTrue(hasEntityKey("dept"));
+ close();
+
+ open();
+ employeesById = store.getPrimaryIndex(String.class, Employee.class);
+ assertTrue(hasEntityKey("salary"));
+ employeesById.put(new SalariedManager("3", "a", "111"));
+ assertTrue(hasEntityKey("salary"));
+ close();
+
+ open();
+ assertTrue(hasEntityKey("dept"));
+ assertTrue(hasEntityKey("salary"));
+ close();
+ }
+
+ private boolean hasEntityKey(String keyName) {
+ return store.getModel().
+ getRawType(Employee.class.getName()).
+ getEntityMetadata().
+ getSecondaryKeys().
+ keySet().
+ contains(keyName);
+ }
+
+ @Entity
+ private static class Employee {
+
+ @PrimaryKey
+ String id;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ String other;
+
+ Employee(String id) {
+ this.id = id;
+ }
+
+ private Employee() {}
+ }
+
+ @Persistent
+ private static class Manager extends Employee {
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ String dept;
+
+ Manager(String id, String dept) {
+ super(id);
+ this.dept = dept;
+ }
+
+ private Manager() {}
+ }
+
+ @Persistent
+ private static class SalariedManager extends Manager {
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ String salary;
+
+ SalariedManager(String id, String dept, String salary) {
+ super(id, dept);
+ this.salary = salary;
+ }
+
+ private SalariedManager() {}
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/util/test/ExceptionWrapperTest.java b/test/scr024/src/com/sleepycat/util/test/ExceptionWrapperTest.java
new file mode 100644
index 00000000..0e09e153
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/util/test/ExceptionWrapperTest.java
@@ -0,0 +1,134 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.util.test;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.IOExceptionWrapper;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * @author Mark Hayes
+ */
+public class ExceptionWrapperTest extends TestCase {
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(ExceptionWrapperTest.class);
+ return suite;
+ }
+
+ public ExceptionWrapperTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+
+ SharedTestUtils.printTestName("ExceptionWrapperTest." + getName());
+ }
+
+ public void testIOWrapper() {
+ try {
+ throw new IOExceptionWrapper(new RuntimeException("msg"));
+ } catch (IOException e) {
+ Exception ee = ExceptionUnwrapper.unwrap(e);
+ assertTrue(ee instanceof RuntimeException);
+ assertEquals("msg", ee.getMessage());
+
+ Throwable t = ExceptionUnwrapper.unwrapAny(e);
+ assertTrue(t instanceof RuntimeException);
+ assertEquals("msg", t.getMessage());
+ }
+ }
+
+ public void testRuntimeWrapper() {
+ try {
+ throw new RuntimeExceptionWrapper(new IOException("msg"));
+ } catch (RuntimeException e) {
+ Exception ee = ExceptionUnwrapper.unwrap(e);
+ assertTrue(ee instanceof IOException);
+ assertEquals("msg", ee.getMessage());
+
+ Throwable t = ExceptionUnwrapper.unwrapAny(e);
+ assertTrue(t instanceof IOException);
+ assertEquals("msg", t.getMessage());
+ }
+ }
+
+ public void testErrorWrapper() {
+ try {
+ throw new RuntimeExceptionWrapper(new Error("msg"));
+ } catch (RuntimeException e) {
+ try {
+ ExceptionUnwrapper.unwrap(e);
+ fail();
+ } catch (Error ee) {
+ assertTrue(ee instanceof Error);
+ assertEquals("msg", ee.getMessage());
+ }
+
+ Throwable t = ExceptionUnwrapper.unwrapAny(e);
+ assertTrue(t instanceof Error);
+ assertEquals("msg", t.getMessage());
+ }
+ }
+
+ /**
+ * Generates a stack trace for a nested exception and checks the output
+ * for the nested exception.
+ */
+ public void testStackTrace() {
+
+ /* Nested stack traces are not avilable in Java 1.3. */
+ String version = System.getProperty("java.version");
+ if (version.startsWith("1.3.")) {
+ return;
+ }
+
+ Exception ex = new Exception("some exception");
+ String causedBy = "Caused by: java.lang.Exception: some exception";
+
+ try {
+ throw new RuntimeExceptionWrapper(ex);
+ } catch (RuntimeException e) {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ String s = sw.toString();
+ assertTrue(s.indexOf(causedBy) != -1);
+ }
+
+ try {
+ throw new IOExceptionWrapper(ex);
+ } catch (IOException e) {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ String s = sw.toString();
+ assertTrue(s.indexOf(causedBy) != -1);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/util/test/FastOutputStreamTest.java b/test/scr024/src/com/sleepycat/util/test/FastOutputStreamTest.java
new file mode 100644
index 00000000..a17ea9b2
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/util/test/FastOutputStreamTest.java
@@ -0,0 +1,66 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.util.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.util.FastOutputStream;
+
+/**
+ * @author Mark Hayes
+ */
+public class FastOutputStreamTest extends TestCase {
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(FastOutputStreamTest.class);
+ return suite;
+ }
+
+ public FastOutputStreamTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+
+ SharedTestUtils.printTestName("FastOutputStreamTest." + getName());
+ }
+
+ public void testBufferSizing() {
+ FastOutputStream fos = new FastOutputStream();
+ assertEquals
+ (FastOutputStream.DEFAULT_INIT_SIZE, fos.getBufferBytes().length);
+
+ /* Write X+1 bytes, expect array size 2X+1 */
+ fos.write(new byte[FastOutputStream.DEFAULT_INIT_SIZE + 1]);
+ assertEquals
+ ((FastOutputStream.DEFAULT_INIT_SIZE * 2) + 1,
+ fos.getBufferBytes().length);
+
+ /* Write X+1 bytes, expect array size 4X+3 = (2(2X+1) + 1) */
+ fos.write(new byte[FastOutputStream.DEFAULT_INIT_SIZE + 1]);
+ assertEquals
+ ((FastOutputStream.DEFAULT_INIT_SIZE * 4) + 3,
+ fos.getBufferBytes().length);
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/util/test/PackedIntegerTest.java b/test/scr024/src/com/sleepycat/util/test/PackedIntegerTest.java
new file mode 100644
index 00000000..46e2a4ec
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/util/test/PackedIntegerTest.java
@@ -0,0 +1,191 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.util.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import com.sleepycat.util.PackedInteger;
+
+public class PackedIntegerTest extends TestCase
+{
+ static final long V119 = 119L;
+ static final long MAX_1 = 0xFFL;
+ static final long MAX_2 = 0xFFFFL;
+ static final long MAX_3 = 0xFFFFFFL;
+ static final long MAX_4 = 0xFFFFFFFFL;
+ static final long MAX_5 = 0xFFFFFFFFFFL;
+ static final long MAX_6 = 0xFFFFFFFFFFFFL;
+ static final long MAX_7 = 0xFFFFFFFFFFFFFFL;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+
+ return new PackedIntegerTest();
+ }
+
+ public PackedIntegerTest() {
+
+ super("PackedIntegerTest");
+ }
+
+ @Override
+ public void runTest() {
+
+ /* Packed int tests. */
+
+ testIntRange(-V119, V119, 1);
+
+ testIntRange(-MAX_1 - V119, -1 - V119, 2);
+ testIntRange(1 + V119, MAX_1 + V119, 2);
+
+ testIntRange(-MAX_2 - V119, -MAX_2 + 99, 3);
+ testIntRange(-MAX_1 - V119 - 99, -MAX_1 - V119 - 1, 3);
+ testIntRange(MAX_1 + V119 + 1, MAX_1 + V119 + 99, 3);
+ testIntRange(MAX_2 - 99, MAX_2 + V119, 3);
+
+ testIntRange(-MAX_3 - V119, -MAX_3 + 99, 4);
+ testIntRange(-MAX_2 - V119 - 99, -MAX_2 - V119 - 1, 4);
+ testIntRange(MAX_2 + V119 + 1, MAX_2 + V119 + 99, 4);
+ testIntRange(MAX_3 - 99, MAX_3 + V119, 4);
+
+ testIntRange(Integer.MIN_VALUE, Integer.MIN_VALUE + 99, 5);
+ testIntRange(Integer.MAX_VALUE - 99, Integer.MAX_VALUE, 5);
+
+ /* Packed long tests. */
+
+ testLongRange(-V119, V119, 1);
+
+ testLongRange(-MAX_1 - V119, -1 - V119, 2);
+ testLongRange(1 + V119, MAX_1 + V119, 2);
+
+ testLongRange(-MAX_2 - V119, -MAX_2 + 99, 3);
+ testLongRange(-MAX_1 - V119 - 99, -MAX_1 - V119 - 1, 3);
+ testLongRange(MAX_1 + V119 + 1, MAX_1 + V119 + 99, 3);
+ testLongRange(MAX_2 - 99, MAX_2 + V119, 3);
+
+ testLongRange(-MAX_3 - V119, -MAX_3 + 99, 4);
+ testLongRange(-MAX_2 - V119 - 99, -MAX_2 - V119 - 1, 4);
+ testLongRange(MAX_2 + V119 + 1, MAX_2 + V119 + 99, 4);
+ testLongRange(MAX_3 - 99, MAX_3 + V119, 4);
+
+ testLongRange(-MAX_4 - V119, -MAX_4 + 99, 5);
+ testLongRange(-MAX_3 - V119 - 99, -MAX_3 - V119 - 1, 5);
+ testLongRange(MAX_3 + V119 + 1, MAX_3 + V119 + 99, 5);
+ testLongRange(MAX_4 - 99, MAX_4 + V119, 5);
+
+ testLongRange(-MAX_5 - V119, -MAX_5 + 99, 6);
+ testLongRange(-MAX_4 - V119 - 99, -MAX_4 - V119 - 1, 6);
+ testLongRange(MAX_4 + V119 + 1, MAX_4 + V119 + 99, 6);
+ testLongRange(MAX_5 - 99, MAX_5 + V119, 6);
+
+ testLongRange(-MAX_6 - V119, -MAX_6 + 99, 7);
+ testLongRange(-MAX_5 - V119 - 99, -MAX_5 - V119 - 1, 7);
+ testLongRange(MAX_5 + V119 + 1, MAX_5 + V119 + 99, 7);
+ testLongRange(MAX_6 - 99, MAX_6 + V119, 7);
+
+ testLongRange(-MAX_7 - V119, -MAX_7 + 99, 8);
+ testLongRange(-MAX_6 - V119 - 99, -MAX_6 - V119 - 1, 8);
+ testLongRange(MAX_6 + V119 + 1, MAX_6 + V119 + 99, 8);
+ testLongRange(MAX_7 - 99, MAX_7 + V119, 8);
+
+ testLongRange(Long.MIN_VALUE, Long.MIN_VALUE + 99, 9);
+ testLongRange(Long.MAX_VALUE - 99, Long.MAX_VALUE - 1, 9);
+ }
+
+ private void testIntRange(long firstValue,
+ long lastValue,
+ int bytesExpected) {
+
+ byte[] buf = new byte[1000];
+ int off = 0;
+
+ for (long longI = firstValue; longI <= lastValue; longI += 1) {
+ int i = (int) longI;
+ int before = off;
+ off = PackedInteger.writeInt(buf, off, i);
+ int bytes = off - before;
+ if (bytes != bytesExpected) {
+ fail("output of value=" + i + " bytes=" + bytes +
+ " bytesExpected=" + bytesExpected);
+ }
+ bytes = PackedInteger.getWriteIntLength(i);
+ if (bytes != bytesExpected) {
+ fail("count of value=" + i + " bytes=" + bytes +
+ " bytesExpected=" + bytesExpected);
+ }
+ }
+
+ off = 0;
+
+ for (long longI = firstValue; longI <= lastValue; longI += 1) {
+ int i = (int) longI;
+ int bytes = PackedInteger.getReadIntLength(buf, off);
+ if (bytes != bytesExpected) {
+ fail("count of value=" + i + " bytes=" + bytes +
+ " bytesExpected=" + bytesExpected);
+ }
+ int value = PackedInteger.readInt(buf, off);
+ if (value != i) {
+ fail("input of value=" + i + " but got=" + value);
+ }
+ off += bytes;
+ }
+ }
+
+ private void testLongRange(long firstValue,
+ long lastValue,
+ int bytesExpected) {
+
+ byte[] buf = new byte[2000];
+ int off = 0;
+
+ for (long longI = firstValue; longI <= lastValue; longI += 1) {
+ long i = longI;
+ int before = off;
+ off = PackedInteger.writeLong(buf, off, i);
+ int bytes = off - before;
+ if (bytes != bytesExpected) {
+ fail("output of value=" + i + " bytes=" + bytes +
+ " bytesExpected=" + bytesExpected);
+ }
+ bytes = PackedInteger.getWriteLongLength(i);
+ if (bytes != bytesExpected) {
+ fail("count of value=" + i + " bytes=" + bytes +
+ " bytesExpected=" + bytesExpected);
+ }
+ }
+
+ off = 0;
+
+ for (long longI = firstValue; longI <= lastValue; longI += 1) {
+ long i = longI;
+ int bytes = PackedInteger.getReadLongLength(buf, off);
+ if (bytes != bytesExpected) {
+ fail("count of value=" + i + " bytes=" + bytes +
+ " bytesExpected=" + bytesExpected);
+ }
+ long value = PackedInteger.readLong(buf, off);
+ if (value != i) {
+ fail("input of value=" + i + " but got=" + value);
+ }
+ off += bytes;
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/util/test/SharedTestUtils.java b/test/scr024/src/com/sleepycat/util/test/SharedTestUtils.java
new file mode 100644
index 00000000..109dd0c3
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/util/test/SharedTestUtils.java
@@ -0,0 +1,178 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.util.test;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.db.DatabaseConfig;
+
+/**
+ * Test utility methods shared by JE and DB core tests. Collections and
+ * persist package test are used in both JE and DB core.
+ */
+public class SharedTestUtils {
+
+ /* Common system properties for running tests */
+ public static String DEST_DIR = "testdestdir";
+ public static String NO_SYNC = "txnnosync";
+ public static String LONG_TEST = "longtest";
+
+ public static final DatabaseConfig DBCONFIG_CREATE = new DatabaseConfig();
+ static {
+ DBCONFIG_CREATE.setAllowCreate(true);
+ }
+
+ private static File getTestDir() {
+ String dir = System.getProperty(DEST_DIR);
+ if (dir == null || dir.length() == 0) {
+ throw new IllegalArgumentException
+ ("System property must be set to test data directory: " +
+ DEST_DIR);
+ }
+ return new File(dir);
+ }
+
+ /**
+ * @return true if long running tests are enabled via setting the system
+ * property longtest=true.
+ */
+ public static boolean runLongTests() {
+ String longTestProp = System.getProperty(LONG_TEST);
+ if ((longTestProp != null) &&
+ longTestProp.equalsIgnoreCase("true")) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static void printTestName(String name) {
+ // don't want verbose printing for now
+ // System.out.println(name);
+ }
+
+ public static File getExistingDir(String name) {
+ File dir = new File(getTestDir(), name);
+ if (!dir.exists() || !dir.isDirectory()) {
+ throw new IllegalStateException(
+ "Not an existing directory: " + dir);
+ }
+ return dir;
+ }
+
+ public static File getNewDir() {
+ return getNewDir("test-dir");
+ }
+
+ public static void emptyDir(File dir) {
+ if (dir.isDirectory()) {
+ String[] files = dir.list();
+ if (files != null) {
+ for (int i = 0; i < files.length; i += 1) {
+ new File(dir, files[i]).delete();
+ }
+ }
+ } else {
+ dir.delete();
+ dir.mkdirs();
+ }
+ }
+
+ public static File getNewDir(String name) {
+ File dir = new File(getTestDir(), name);
+ emptyDir(dir);
+ return dir;
+ }
+
+ public static File getNewFile() {
+ return getNewFile("test-file");
+ }
+
+ public static File getNewFile(String name) {
+ return getNewFile(getTestDir(), name);
+ }
+
+ public static File getNewFile(File dir, String name) {
+ File file = new File(dir, name);
+ file.delete();
+ return file;
+ }
+
+ public static boolean copyResource(Class cls, String fileName, File toDir)
+ throws IOException {
+
+ InputStream in = cls.getResourceAsStream("testdata/" + fileName);
+ if (in == null) {
+ return false;
+ }
+ in = new BufferedInputStream(in);
+ File file = new File(toDir, fileName);
+ OutputStream out = new FileOutputStream(file);
+ out = new BufferedOutputStream(out);
+ int c;
+ while ((c = in.read()) >= 0) out.write(c);
+ in.close();
+ out.close();
+ return true;
+ }
+
+ public static String qualifiedTestName(TestCase test) {
+
+ String s = test.getClass().getName();
+ int i = s.lastIndexOf('.');
+ if (i >= 0) {
+ s = s.substring(i + 1);
+ }
+ return s + '.' + test.getName();
+ }
+
+ /**
+ * Copies all files in fromDir to toDir. Does not copy subdirectories.
+ */
+ public static void copyFiles(File fromDir, File toDir)
+ throws IOException {
+
+ String[] names = fromDir.list();
+ if (names != null) {
+ for (int i = 0; i < names.length; i += 1) {
+ File fromFile = new File(fromDir, names[i]);
+ if (fromFile.isDirectory()) {
+ continue;
+ }
+ File toFile = new File(toDir, names[i]);
+ int len = (int) fromFile.length();
+ byte[] data = new byte[len];
+ FileInputStream fis = null;
+ FileOutputStream fos = null;
+ try {
+ fis = new FileInputStream(fromFile);
+ fos = new FileOutputStream(toFile);
+ fis.read(data);
+ fos.write(data);
+ } finally {
+ if (fis != null) {
+ fis.close();
+ }
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/util/test/TestEnv.java b/test/scr024/src/com/sleepycat/util/test/TestEnv.java
new file mode 100644
index 00000000..d065a43b
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/util/test/TestEnv.java
@@ -0,0 +1,142 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.util.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+/**
+ * @author Mark Hayes
+ */
+public class TestEnv {
+
+ public static final TestEnv BDB;
+ public static final TestEnv CDB;
+ public static final TestEnv TXN;
+ static {
+ EnvironmentConfig config;
+
+ config = newEnvConfig();
+ BDB = new TestEnv("bdb", config);
+
+ if (DbCompat.CDB) {
+ config = newEnvConfig();
+ DbCompat.setInitializeCDB(config, true);
+ CDB = new TestEnv("cdb", config);
+ } else {
+ CDB = null;
+ }
+
+ config = newEnvConfig();
+ config.setTransactional(true);
+ DbCompat.setInitializeLocking(config, true);
+ TXN = new TestEnv("txn", config);
+ }
+
+ private static EnvironmentConfig newEnvConfig() {
+
+ EnvironmentConfig config = new EnvironmentConfig();
+ config.setTxnNoSync(Boolean.getBoolean(SharedTestUtils.NO_SYNC));
+ if (DbCompat.MEMORY_SUBSYSTEM) {
+ DbCompat.setInitializeCache(config, true);
+ }
+ return config;
+ }
+
+ public static final TestEnv[] ALL;
+ static {
+ if (DbCompat.CDB) {
+ ALL = new TestEnv[] { BDB, CDB, TXN };
+ } else {
+ ALL = new TestEnv[] { BDB, TXN };
+ }
+ }
+
+ private final String name;
+ private final EnvironmentConfig config;
+
+ protected TestEnv(String name, EnvironmentConfig config) {
+
+ this.name = name;
+ this.config = config;
+ }
+
+ public String getName() {
+
+ return name;
+ }
+
+ public EnvironmentConfig getConfig() {
+ return config;
+ }
+
+ void copyConfig(EnvironmentConfig copyToConfig) {
+ DbCompat.setInitializeCache
+ (copyToConfig, DbCompat.getInitializeCache(config));
+ DbCompat.setInitializeLocking
+ (copyToConfig, DbCompat.getInitializeLocking(config));
+ DbCompat.setInitializeCDB
+ (copyToConfig, DbCompat.getInitializeCDB(config));
+ copyToConfig.setTransactional(config.getTransactional());
+ }
+
+ public boolean isTxnMode() {
+
+ return config.getTransactional();
+ }
+
+ public boolean isCdbMode() {
+
+ return DbCompat.getInitializeCDB(config);
+ }
+
+ public Environment open(String testName)
+ throws IOException, DatabaseException {
+
+ return open(testName, true);
+ }
+
+ public Environment open(String testName, boolean create)
+ throws IOException, DatabaseException {
+
+ config.setAllowCreate(create);
+ /* OLDEST deadlock detection on DB matches the use of timeouts on JE.*/
+ DbCompat.setLockDetectModeOldest(config);
+ File dir = getDirectory(testName, create);
+ return newEnvironment(dir, config);
+ }
+
+ /**
+ * Is overridden in XACollectionTest.
+ * @throws FileNotFoundException from DB core.
+ */
+ protected Environment newEnvironment(File dir, EnvironmentConfig config)
+ throws DatabaseException, FileNotFoundException {
+
+ return new Environment(dir, config);
+ }
+
+ public File getDirectory(String testName) {
+ return getDirectory(testName, true);
+ }
+
+ public File getDirectory(String testName, boolean create) {
+ if (create) {
+ return SharedTestUtils.getNewDir(testName);
+ } else {
+ return SharedTestUtils.getExistingDir(testName);
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/util/test/TxnTestCase.java b/test/scr024/src/com/sleepycat/util/test/TxnTestCase.java
new file mode 100644
index 00000000..249ad2c8
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/util/test/TxnTestCase.java
@@ -0,0 +1,230 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.util.test;
+
+import java.io.File;
+import java.util.Enumeration;
+
+import junit.framework.TestSuite;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.db.TransactionConfig;
+import com.sleepycat.db.util.DualTestCase;
+
+/**
+ * Permutes test cases over three transaction types: null (non-transactional),
+ * auto-commit, and user (explicit).
+ *
+ * <p>Overrides runTest, setUp and tearDown to open/close the environment and
+ * to set up protected members for use by test cases.</p>
+ *
+ * <p>If a subclass needs to override setUp or tearDown, the overridden method
+ * should call super.setUp or super.tearDown.</p>
+ *
+ * <p>When writing a test case based on this class, write it as if a user txn
+ * were always used: call txnBegin, txnCommit and txnAbort for all write
+ * operations. Use the isTransactional protected field for setup of a database
+ * config.</p>
+ */
+public abstract class TxnTestCase extends DualTestCase {
+
+ public static final String TXN_NULL = "txn-null";
+ public static final String TXN_AUTO = "txn-auto";
+ public static final String TXN_USER = "txn-user";
+
+ protected File envHome;
+ protected Environment env;
+ protected EnvironmentConfig envConfig;
+ protected String txnType;
+ protected boolean isTransactional;
+
+ /**
+ * Returns a txn test suite. If txnTypes is null, all three types are run.
+ */
+ public static TestSuite txnTestSuite(Class<?> testCaseClass,
+ EnvironmentConfig envConfig,
+ String[] txnTypes) {
+ if (txnTypes == null) {
+ txnTypes =
+ isReplicatedTest(testCaseClass) ?
+ new String[] { // Skip non-transactional tests
+ TxnTestCase.TXN_USER,
+ TxnTestCase.TXN_AUTO } :
+ new String[] { TxnTestCase.TXN_NULL,
+ TxnTestCase.TXN_USER,
+ TxnTestCase.TXN_AUTO } ;
+ }
+ if (envConfig == null) {
+ envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ }
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < txnTypes.length; i += 1) {
+ TestSuite baseSuite = new TestSuite(testCaseClass);
+ Enumeration e = baseSuite.tests();
+ while (e.hasMoreElements()) {
+ TxnTestCase test = (TxnTestCase) e.nextElement();
+ test.txnInit(envConfig, txnTypes[i]);
+ suite.addTest(test);
+ }
+ }
+ return suite;
+ }
+
+ private void txnInit(EnvironmentConfig envConfig, String txnType) {
+
+ this.envConfig = envConfig;
+ this.txnType = txnType;
+ isTransactional = (txnType != TXN_NULL);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ super.setUp();
+ envHome = SharedTestUtils.getNewDir();
+ }
+
+ @Override
+ public void runTest()
+ throws Throwable {
+
+ openEnv();
+ super.runTest();
+ closeEnv();
+ }
+
+ @Override
+ public void tearDown()
+ throws Exception {
+
+ /* Set test name for reporting; cannot be done in the ctor or setUp. */
+ setName(txnType + ':' + getName());
+
+ super.tearDown();
+ env = null;
+
+ try {
+ SharedTestUtils.emptyDir(envHome);
+ } catch (Throwable e) {
+ System.out.println("tearDown: " + e);
+ }
+ }
+
+ /**
+ * Closes the environment and sets the env field to null.
+ * Used for closing and reopening the environment.
+ */
+ public void closeEnv()
+ throws DatabaseException {
+
+ if (env != null) {
+ close(env);
+ env = null;
+ }
+ }
+
+ /**
+ * Opens the environment based on the txnType for this test case.
+ * Used for closing and reopening the environment.
+ */
+ public void openEnv()
+ throws DatabaseException {
+
+ if (txnType == TXN_NULL) {
+ TestEnv.BDB.copyConfig(envConfig);
+ env = create(envHome, envConfig);
+ } else if (txnType == TXN_AUTO) {
+ TestEnv.TXN.copyConfig(envConfig);
+ env = create(envHome, envConfig);
+ } else if (txnType == TXN_USER) {
+ TestEnv.TXN.copyConfig(envConfig);
+ env = create(envHome, envConfig);
+ } else {
+ assert false;
+ }
+ }
+
+ /**
+ * Begin a txn if in TXN_USER mode; otherwise return null;
+ */
+ protected Transaction txnBegin()
+ throws DatabaseException {
+
+ return txnBegin(null, null);
+ }
+
+ /**
+ * Begin a txn if in TXN_USER mode; otherwise return null;
+ */
+ protected Transaction txnBegin(Transaction parentTxn,
+ TransactionConfig config)
+ throws DatabaseException {
+
+ /*
+ * Replicated tests need a user txn for auto txns args to
+ * Database.get/put methods.
+ */
+ if (txnType == TXN_USER ||
+ (isReplicatedTest(getClass()) && txnType == TXN_AUTO)) {
+ return env.beginTransaction(parentTxn, config);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Begin a txn if in TXN_USER or TXN_AUTO mode; otherwise return null;
+ */
+ protected Transaction txnBeginCursor()
+ throws DatabaseException {
+
+ return txnBeginCursor(null, null);
+ }
+
+ /**
+ * Begin a txn if in TXN_USER or TXN_AUTO mode; otherwise return null;
+ */
+ protected Transaction txnBeginCursor(Transaction parentTxn,
+ TransactionConfig config)
+ throws DatabaseException {
+
+ if (txnType == TXN_USER || txnType == TXN_AUTO) {
+ return env.beginTransaction(parentTxn, config);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Commit a txn if non-null.
+ */
+ protected void txnCommit(Transaction txn)
+ throws DatabaseException {
+
+ if (txn != null) {
+ txn.commit();
+ }
+ }
+
+ /**
+ * Commit a txn if non-null.
+ */
+ protected void txnAbort(Transaction txn)
+ throws DatabaseException {
+
+ if (txn != null) {
+ txn.abort();
+ }
+ }
+}
diff --git a/test/scr024/src/com/sleepycat/util/test/UtfTest.java b/test/scr024/src/com/sleepycat/util/test/UtfTest.java
new file mode 100644
index 00000000..7b0ef530
--- /dev/null
+++ b/test/scr024/src/com/sleepycat/util/test/UtfTest.java
@@ -0,0 +1,163 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.util.test;
+
+import java.io.DataOutputStream;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.util.FastOutputStream;
+import com.sleepycat.util.UtfOps;
+
+/**
+ * @author Mark Hayes
+ */
+public class UtfTest extends TestCase {
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(UtfTest.class);
+ return suite;
+ }
+
+ public UtfTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+
+ SharedTestUtils.printTestName("UtfTest." + getName());
+ }
+
+ /**
+ * Compares the UtfOps implementation to the java.util.DataOutputStream
+ * (and by implication DataInputStream) implementation, character for
+ * character in the full Unicode set.
+ */
+ public void testMultibyte()
+ throws Exception {
+
+ char c = 0;
+ byte[] buf = new byte[10];
+ byte[] javaBuf = new byte[10];
+ char[] cArray = new char[1];
+ FastOutputStream javaBufStream = new FastOutputStream(javaBuf);
+ DataOutputStream javaOutStream = new DataOutputStream(javaBufStream);
+
+ try {
+ for (int cInt = Character.MIN_VALUE; cInt <= Character.MAX_VALUE;
+ cInt += 1) {
+ c = (char) cInt;
+ cArray[0] = c;
+ int byteLen = UtfOps.getByteLength(cArray);
+
+ javaBufStream.reset();
+ javaOutStream.writeUTF(new String(cArray));
+ int javaByteLen = javaBufStream.size() - 2;
+
+ if (byteLen != javaByteLen) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " UtfOps size " + byteLen +
+ " != JavaIO size " + javaByteLen);
+ }
+
+ Arrays.fill(buf, (byte) 0);
+ UtfOps.charsToBytes(cArray, 0, buf, 0, 1);
+
+ if (byteLen == 1 && buf[0] == (byte) 0xff) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " was encoded as FF, which is reserved for null");
+ }
+
+ for (int i = 0; i < byteLen; i += 1) {
+ if (buf[i] != javaBuf[i + 2]) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " byte offset " + i +
+ " UtfOps byte " + Integer.toHexString(buf[i]) +
+ " != JavaIO byte " +
+ Integer.toHexString(javaBuf[i + 2]));
+ }
+ }
+
+ int charLen = UtfOps.getCharLength(buf, 0, byteLen);
+ if (charLen != 1) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " UtfOps char len " + charLen +
+ " but should be one");
+ }
+
+ cArray[0] = (char) 0;
+ int len = UtfOps.bytesToChars(buf, 0, cArray, 0, byteLen,
+ true);
+ if (len != byteLen) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " UtfOps bytesToChars(w/byteLen) len " + len +
+ " but should be " + byteLen);
+ }
+
+ if (cArray[0] != c) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " UtfOps bytesToChars(w/byteLen) char " +
+ Integer.toHexString(cArray[0]));
+ }
+
+ cArray[0] = (char) 0;
+ len = UtfOps.bytesToChars(buf, 0, cArray, 0, 1, false);
+ if (len != byteLen) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " UtfOps bytesToChars(w/charLen) len " + len +
+ " but should be " + byteLen);
+ }
+
+ if (cArray[0] != c) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " UtfOps bytesToChars(w/charLen) char " +
+ Integer.toHexString(cArray[0]));
+ }
+
+ String s = new String(cArray, 0, 1);
+ byte[] sBytes = UtfOps.stringToBytes(s);
+ if (sBytes.length != byteLen) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " UtfOps stringToBytes() len " + sBytes.length +
+ " but should be " + byteLen);
+ }
+
+ for (int i = 0; i < byteLen; i += 1) {
+ if (sBytes[i] != javaBuf[i + 2]) {
+ fail("Character 0x" + Integer.toHexString(c) +
+ " byte offset " + i +
+ " UtfOps byte " + Integer.toHexString(sBytes[i]) +
+ " != JavaIO byte " +
+ Integer.toHexString(javaBuf[i + 2]));
+ }
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("Character 0x" + Integer.toHexString(c) +
+ " exception occurred");
+ throw e;
+ }
+ }
+}