/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. * */ 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 static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.FileNotFoundException; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.math.BigDecimal; 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.LinkedHashMap; 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 org.junit.After; import org.junit.Before; import org.junit.Test; 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.RefreshException; 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.TestBase; import com.sleepycat.util.test.TestEnv; /** * @author Mark Hayes */ public class BindingTest extends TestBase { 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; @Before public void setUp() throws Exception { envHome = SharedTestUtils.getTestDir(); super.setUp(); keyEntry = new DatabaseEntry(); dataEntry = new DatabaseEntry(); } @After 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 (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; } @Test 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)); } @Override public String toString() { return "" + id + ' ' + one + ' ' + two; } } @Test 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 = new BigDecimal("123.1234000"); 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); /* The sorted BigDecimal cannot preserve the precision. */ TestCase.assertTrue(!f10.equals(o.f10)); TestCase.assertEquals(f10.compareTo(o.f10), 0); TestCase.assertEquals(f10.stripTrailingZeros(), 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); } } @Test 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)); } } @Test 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()}, {"f4", MyEnumCSM.class.getName()}, {"f5", Object.class.getName()}, }, 0 /*priKeyIndex*/, null); close(); } enum MyEnum { ONE, TWO }; enum MyEnumCSM { A { void Foo() { System.out.println("This is A!"); } }, B(true) { void Foo() { System.out.println("This is B!"); } }; private boolean b; MyEnumCSM() { this(false); } MyEnumCSM(boolean b) { this.b = b; } abstract void Foo(); }; @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; private final MyEnumCSM f4 = MyEnumCSM.A; private final Object f5 = MyEnumCSM.B; 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); TestCase.assertSame(f4, o.f4); TestCase.assertSame(f5, o.f5); } } @Test public void testEnumObjectTypes() throws FileNotFoundException, DatabaseException { open(); checkEntity(EnumObjectTypes.class, new EnumObjectTypes()); checkMetadata(EnumObjectTypes.class.getName(), new String[][] { {"f0", "int"}, {"f1", Object.class.getName()}, {"f2", Object.class.getName()}, }, 0 /*priKeyIndex*/, null); close(); } @Entity static class EnumObjectTypes implements MyEntity { @PrimaryKey private final int f0 = 1; private final Object f1 = MyEnum.ONE; private final Object f2 = MyEnumCSM.A; EnumObjectTypes() { } public Object getPriKeyObject() { return f0; } public void validate(Object other) { EnumObjectTypes o = (EnumObjectTypes) other; TestCase.assertEquals(f0, o.f0); TestCase.assertSame(f1, o.f1); TestCase.assertSame(f2, o.f2); } } @Test 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()}, {"f10", LinkedHashMap.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"); private final LinkedHashMap<String, Integer> f10 = new LinkedHashMap<String, Integer>(); 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); f10.put("one", 111); f10.put("two", 222); f10.put("three", 333); } 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); TestCase.assertEquals(f10, o.f10); } } @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); } } @Test 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); } 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; } } @Test 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); } } @Test 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); } } @Test 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); 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()); } } @Test public void testCompositeKey() throws FileNotFoundException, DatabaseException { open(); CompositeKey key = new CompositeKey(123, 456L, "xyz", BigInteger.valueOf(789), MyEnum.ONE, MyEnumCSM.A, BigDecimal.valueOf(123.123)); 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()}, {"f6", MyEnumCSM.class.getName()}, {"f7", BigDecimal.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; @KeyField(6) private MyEnumCSM f6; @KeyField(7) private BigDecimal f7; private CompositeKey() {} CompositeKey(int f1, Long f2, String f3, BigInteger f4, MyEnum f5, MyEnumCSM f6, BigDecimal f7) { this.f1 = f1; this.f2 = f2; this.f3 = f3; this.f4 = f4; this.f5 = f5; this.f6 = f6; this.f7 = f7; } 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)); TestCase.assertEquals(f5, o.f5); TestCase.assertTrue(nullOrEqual(f6, o.f6)); TestCase.assertTrue(nullOrEqual(f7, o.f7)); } @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) && nullOrEqual(f6, o.f6) && nullOrEqual(f7, o.f7); } @Override public int hashCode() { return f1; } @Override public String toString() { return "" + f1 + ' ' + f2 + ' ' + f3 + ' ' + f4 + ' ' + f5 + ' ' + f6 + ' ' + f7; } } @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)); } } @Test 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(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)); } } @Test 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()}, {"f41", MyEnumCSM.class.getName()}, {"g41", MyEnumCSM.class.getName()}, {"f42", MyEnumCSM[].class.getName()}, {"g42", MyEnumCSM[].class.getName()}, {"f43", Set.class.getName()}, {"g43", Set.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); checkSecKey(obj, "f41", obj.f41, MyEnumCSM.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); checkSecMultiKey(obj, "f42", toSet(obj.f42), MyEnumCSM.class); checkSecMultiKey(obj, "f43", toSet(obj.f43), MyEnumCSM.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); nullifySecKey(obj, "f41", obj.f41, MyEnumCSM.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); nullifySecMultiKey(obj, "f42", obj.f42, MyEnumCSM.class); nullifySecMultiKey(obj, "f43", obj.f43, MyEnumCSM.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, MyEnumCSM.A, BigDecimal.valueOf(123.123)); private final CompositeKey g20 = new CompositeKey(20, 20L, "20", BigInteger.valueOf(20), MyEnum.TWO, MyEnumCSM.B, BigDecimal.valueOf(123.123)); 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, MyEnumCSM.A, BigDecimal.valueOf(123.123)), new CompositeKey(101, 101L, "101", BigInteger.valueOf(101), MyEnum.TWO, MyEnumCSM.B, BigDecimal.valueOf(123.123)), new CompositeKey(102, 102L, "102", BigInteger.valueOf(102), MyEnum.TWO, MyEnumCSM.B, BigDecimal.valueOf(123.123)), }; private static MyEnum[] arrayOfEnum = new MyEnum[] { MyEnum.ONE, MyEnum.TWO }; private static MyEnumCSM[] arrayOfEnumCSM = new MyEnumCSM[] { MyEnumCSM.A, MyEnumCSM.B }; @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; @SecondaryKey(relate=MANY_TO_ONE) private final MyEnumCSM f41 = MyEnumCSM.B; private final MyEnumCSM g41 = f41; @SecondaryKey(relate=ONE_TO_MANY) private final MyEnumCSM[] f42 = arrayOfEnumCSM; private final MyEnumCSM[] g42 = f42; @SecondaryKey(relate=ONE_TO_MANY) private final Set<MyEnumCSM> f43 = toSet(arrayOfEnumCSM); private final Set<MyEnumCSM> g43 = f43; /* 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(f26, o.f26); TestCase.assertTrue(Arrays.equals(f27, o.f27)); TestCase.assertEquals(f28, o.f28); TestCase.assertEquals(f41, o.f41); TestCase.assertTrue(Arrays.equals(f42, o.f42)); TestCase.assertEquals(f43, o.f43); 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(g26, o.g26); TestCase.assertTrue(Arrays.equals(g27, o.g27)); TestCase.assertEquals(g28, o.g28); TestCase.assertEquals(g41, o.g41); TestCase.assertTrue(Arrays.equals(g42, o.g42)); TestCase.assertEquals(g43, o.g43); 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); } } @Test 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()); } } @Test 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); } } @Test 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. */ try { entity2 = catalog.convertRawObject(rawEntity, null); } catch (RefreshException e) { fail(e.toString()); } 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 = null; storedCatalog = new PersistCatalog (env, STORE_PREFIX, STORE_PREFIX + "catalog", new DatabaseConfig(), 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 = checkCatalog.resolveClass(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) { StringBuilder buf = new StringBuilder(); 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()); } } }