1 /*-
2  * Copyright (c) 2002, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
5  *
6  */
7 
8 package com.sleepycat.persist.test;
9 
10 import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
11 import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
12 import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertNotNull;
15 import static org.junit.Assert.assertNull;
16 import static org.junit.Assert.assertSame;
17 import static org.junit.Assert.assertTrue;
18 import static org.junit.Assert.fail;
19 
20 import java.io.File;
21 import java.io.FileNotFoundException;
22 import java.lang.reflect.Array;
23 import java.lang.reflect.Field;
24 import java.math.BigDecimal;
25 import java.math.BigInteger;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Comparator;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.LinkedHashMap;
35 import java.util.LinkedList;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.TreeMap;
41 import java.util.TreeSet;
42 
43 import junit.framework.TestCase;
44 
45 import org.junit.After;
46 import org.junit.Before;
47 import org.junit.Test;
48 
49 import com.sleepycat.bind.EntryBinding;
50 import com.sleepycat.compat.DbCompat;
51 import com.sleepycat.db.DatabaseConfig;
52 import com.sleepycat.db.DatabaseEntry;
53 import com.sleepycat.db.DatabaseException;
54 import com.sleepycat.db.Environment;
55 import com.sleepycat.db.EnvironmentConfig;
56 import com.sleepycat.db.ForeignMultiKeyNullifier;
57 import com.sleepycat.db.SecondaryKeyCreator;
58 import com.sleepycat.db.SecondaryMultiKeyCreator;
59 import com.sleepycat.persist.impl.PersistCatalog;
60 import com.sleepycat.persist.impl.PersistComparator;
61 import com.sleepycat.persist.impl.PersistEntityBinding;
62 import com.sleepycat.persist.impl.PersistKeyBinding;
63 import com.sleepycat.persist.impl.PersistKeyCreator;
64 import com.sleepycat.persist.impl.RefreshException;
65 import com.sleepycat.persist.model.AnnotationModel;
66 import com.sleepycat.persist.model.ClassMetadata;
67 import com.sleepycat.persist.model.Entity;
68 import com.sleepycat.persist.model.EntityMetadata;
69 import com.sleepycat.persist.model.EntityModel;
70 import com.sleepycat.persist.model.KeyField;
71 import com.sleepycat.persist.model.Persistent;
72 import com.sleepycat.persist.model.PersistentProxy;
73 import com.sleepycat.persist.model.PrimaryKey;
74 import com.sleepycat.persist.model.PrimaryKeyMetadata;
75 import com.sleepycat.persist.model.SecondaryKey;
76 import com.sleepycat.persist.model.SecondaryKeyMetadata;
77 import com.sleepycat.persist.raw.RawField;
78 import com.sleepycat.persist.raw.RawObject;
79 import com.sleepycat.persist.raw.RawType;
80 import com.sleepycat.util.test.SharedTestUtils;
81 import com.sleepycat.util.test.TestBase;
82 import com.sleepycat.util.test.TestEnv;
83 
84 /**
85  * @author Mark Hayes
86  */
87 public class BindingTest extends TestBase {
88 
89     private static final String STORE_PREFIX = "persist#foo#";
90 
91     private File envHome;
92     private Environment env;
93     private EntityModel model;
94     private PersistCatalog catalog;
95     private DatabaseEntry keyEntry;
96     private DatabaseEntry dataEntry;
97 
98     @Before
setUp()99     public void setUp()
100         throws Exception {
101 
102         envHome = SharedTestUtils.getTestDir();
103         super.setUp();
104 
105         keyEntry = new DatabaseEntry();
106         dataEntry = new DatabaseEntry();
107     }
108 
109     @After
tearDown()110     public void tearDown() {
111         if (env != null) {
112             try {
113                 env.close();
114             } catch (Exception e) {
115                 System.out.println("During tearDown: " + e);
116             }
117         }
118         envHome = null;
119         env = null;
120         catalog = null;
121         keyEntry = null;
122         dataEntry = null;
123     }
124 
125     /**
126      * @throws FileNotFoundException from DB core.
127      */
open()128     private void open()
129         throws FileNotFoundException, DatabaseException {
130 
131         EnvironmentConfig envConfig = TestEnv.BDB.getConfig();
132         envConfig.setAllowCreate(true);
133         env = new Environment(envHome, envConfig);
134         openCatalog();
135     }
136 
openCatalog()137     private void openCatalog()
138         throws DatabaseException {
139 
140         model = new AnnotationModel();
141         model.registerClass(LocalizedTextProxy.class);
142         model.registerClass(LocaleProxy.class);
143 
144         DatabaseConfig dbConfig = new DatabaseConfig();
145         dbConfig.setAllowCreate(true);
146         DbCompat.setTypeBtree(dbConfig);
147         catalog = new PersistCatalog
148             (env, STORE_PREFIX, STORE_PREFIX + "catalog", dbConfig, model,
149              null, false /*rawAccess*/, null /*Store*/);
150     }
151 
close()152     private void close()
153         throws DatabaseException {
154 
155         /* Close/open/close catalog to test checks for class evolution. */
156         catalog.close();
157         PersistCatalog.expectNoClassChanges = true;
158         try {
159             openCatalog();
160         } finally {
161             PersistCatalog.expectNoClassChanges = false;
162         }
163         catalog.close();
164         catalog = null;
165 
166         env.close();
167         env = null;
168     }
169 
170     @Test
testBasic()171     public void testBasic()
172         throws FileNotFoundException, DatabaseException {
173 
174         open();
175 
176         checkEntity(Basic.class,
177                     new Basic(1, "one", 2.2, "three"));
178         checkEntity(Basic.class,
179                     new Basic(0, null, 0, null));
180         checkEntity(Basic.class,
181                     new Basic(-1, "xxx", -2, "xxx"));
182 
183         checkMetadata(Basic.class.getName(), new String[][] {
184                           {"id", "long"},
185                           {"one", "java.lang.String"},
186                           {"two", "double"},
187                           {"three", "java.lang.String"},
188                       },
189                       0 /*priKeyIndex*/, null);
190 
191         close();
192     }
193 
194     @Entity
195     static class Basic implements MyEntity {
196 
197         @PrimaryKey
198         private long id;
199         private String one;
200         private double two;
201         private String three;
202 
Basic()203         private Basic() { }
204 
Basic(long id, String one, double two, String three)205         private Basic(long id, String one, double two, String three) {
206             this.id = id;
207             this.one = one;
208             this.two = two;
209             this.three = three;
210         }
211 
getBasicOne()212         public String getBasicOne() {
213             return one;
214         }
215 
getPriKeyObject()216         public Object getPriKeyObject() {
217             return id;
218         }
219 
validate(Object other)220         public void validate(Object other) {
221             Basic o = (Basic) other;
222             TestCase.assertEquals(id, o.id);
223             TestCase.assertTrue(nullOrEqual(one, o.one));
224             TestCase.assertEquals(two, o.two);
225             TestCase.assertTrue(nullOrEqual(three, o.three));
226         }
227 
228         @Override
toString()229         public String toString() {
230             return "" + id + ' ' + one + ' ' + two;
231         }
232     }
233 
234     @Test
testSimpleTypes()235     public void testSimpleTypes()
236         throws FileNotFoundException, DatabaseException {
237 
238         open();
239 
240         checkEntity(SimpleTypes.class, new SimpleTypes());
241 
242         checkMetadata(SimpleTypes.class.getName(), new String[][] {
243                           {"f0", "boolean"},
244                           {"f1", "char"},
245                           {"f2", "byte"},
246                           {"f3", "short"},
247                           {"f4", "int"},
248                           {"f5", "long"},
249                           {"f6", "float"},
250                           {"f7", "double"},
251                           {"f8", "java.lang.String"},
252                           {"f9", "java.math.BigInteger"},
253                           {"f10", "java.math.BigDecimal"},
254                           {"f11", "java.util.Date"},
255                           {"f12", "java.lang.Boolean"},
256                           {"f13", "java.lang.Character"},
257                           {"f14", "java.lang.Byte"},
258                           {"f15", "java.lang.Short"},
259                           {"f16", "java.lang.Integer"},
260                           {"f17", "java.lang.Long"},
261                           {"f18", "java.lang.Float"},
262                           {"f19", "java.lang.Double"},
263                       },
264                       0 /*priKeyIndex*/, null);
265 
266         close();
267     }
268 
269     @Entity
270     static class SimpleTypes implements MyEntity {
271 
272         @PrimaryKey
273         private final boolean f0 = true;
274         private final char f1 = 'a';
275         private final byte f2 = 123;
276         private final short f3 = 123;
277         private final int f4 = 123;
278         private final long f5 = 123;
279         private final float f6 = 123.4f;
280         private final double f7 = 123.4;
281         private final String f8 = "xxx";
282         private final BigInteger f9 = BigInteger.valueOf(123);
283         private BigDecimal f10 = new BigDecimal("123.1234000");
284         private final Date f11 = new Date();
285         private final Boolean f12 = true;
286         private final Character f13 = 'a';
287         private final Byte f14 = 123;
288         private final Short f15 = 123;
289         private final Integer f16 = 123;
290         private final Long f17 = 123L;
291         private final Float f18 = 123.4f;
292         private final Double f19 = 123.4;
293 
SimpleTypes()294         SimpleTypes() { }
295 
getPriKeyObject()296         public Object getPriKeyObject() {
297             return f0;
298         }
299 
validate(Object other)300         public void validate(Object other) {
301             SimpleTypes o = (SimpleTypes) other;
302             TestCase.assertEquals(f0, o.f0);
303             TestCase.assertEquals(f1, o.f1);
304             TestCase.assertEquals(f2, o.f2);
305             TestCase.assertEquals(f3, o.f3);
306             TestCase.assertEquals(f4, o.f4);
307             TestCase.assertEquals(f5, o.f5);
308             TestCase.assertEquals(f6, o.f6);
309             TestCase.assertEquals(f7, o.f7);
310             TestCase.assertEquals(f8, o.f8);
311             TestCase.assertEquals(f9, o.f9);
312             /* The sorted BigDecimal cannot preserve the precision. */
313             TestCase.assertTrue(!f10.equals(o.f10));
314             TestCase.assertEquals(f10.compareTo(o.f10), 0);
315             TestCase.assertEquals(f10.stripTrailingZeros(), o.f10);
316             TestCase.assertEquals(f11, o.f11);
317             TestCase.assertEquals(f12, o.f12);
318             TestCase.assertEquals(f13, o.f13);
319             TestCase.assertEquals(f14, o.f14);
320             TestCase.assertEquals(f15, o.f15);
321             TestCase.assertEquals(f16, o.f16);
322             TestCase.assertEquals(f17, o.f17);
323             TestCase.assertEquals(f18, o.f18);
324             TestCase.assertEquals(f19, o.f19);
325         }
326     }
327 
328     @Test
testArrayTypes()329     public void testArrayTypes()
330         throws FileNotFoundException, DatabaseException {
331 
332         open();
333 
334         checkEntity(ArrayTypes.class, new ArrayTypes());
335 
336         checkMetadata(ArrayTypes.class.getName(), new String[][] {
337                           {"id", "int"},
338                           {"f0", boolean[].class.getName()},
339                           {"f1", char[].class.getName()},
340                           {"f2", byte[].class.getName()},
341                           {"f3", short[].class.getName()},
342                           {"f4", int[].class.getName()},
343                           {"f5", long[].class.getName()},
344                           {"f6", float[].class.getName()},
345                           {"f7", double[].class.getName()},
346                           {"f8", String[].class.getName()},
347                           {"f9", Address[].class.getName()},
348                           {"f10", boolean[][][].class.getName()},
349                           {"f11", String[][][].class.getName()},
350                       },
351                       0 /*priKeyIndex*/, null);
352 
353         close();
354     }
355 
356     @Entity
357     static class ArrayTypes implements MyEntity {
358 
359         @PrimaryKey
360         private final int id = 1;
361         private final boolean[] f0 =  {false, true};
362         private final char[] f1 = {'a', 'b'};
363         private final byte[] f2 = {1, 2};
364         private final short[] f3 = {1, 2};
365         private final int[] f4 = {1, 2};
366         private final long[] f5 = {1, 2};
367         private final float[] f6 = {1.1f, 2.2f};
368         private final double[] f7 = {1.1, 2,2};
369         private final String[] f8 = {"xxx", null, "yyy"};
370         private final Address[] f9 = {new Address("city", "state", 123),
371                                 null,
372                                 new Address("x", "y", 444)};
373         private final boolean[][][] f10 =
374         {
375             {
376                 {false, true},
377                 {false, true},
378             },
379             null,
380             {
381                 {false, true},
382                 {false, true},
383             },
384         };
385         private final String[][][] f11 =
386         {
387             {
388                 {"xxx", null, "yyy"},
389                 null,
390                 {"xxx", null, "yyy"},
391             },
392             null,
393             {
394                 {"xxx", null, "yyy"},
395                 null,
396                 {"xxx", null, "yyy"},
397             },
398         };
399 
ArrayTypes()400         ArrayTypes() { }
401 
getPriKeyObject()402         public Object getPriKeyObject() {
403             return id;
404         }
405 
validate(Object other)406         public void validate(Object other) {
407             ArrayTypes o = (ArrayTypes) other;
408             TestCase.assertEquals(id, o.id);
409             TestCase.assertTrue(Arrays.equals(f0, o.f0));
410             TestCase.assertTrue(Arrays.equals(f1, o.f1));
411             TestCase.assertTrue(Arrays.equals(f2, o.f2));
412             TestCase.assertTrue(Arrays.equals(f3, o.f3));
413             TestCase.assertTrue(Arrays.equals(f4, o.f4));
414             TestCase.assertTrue(Arrays.equals(f5, o.f5));
415             TestCase.assertTrue(Arrays.equals(f6, o.f6));
416             TestCase.assertTrue(Arrays.equals(f7, o.f7));
417             TestCase.assertTrue(Arrays.equals(f8, o.f8));
418             TestCase.assertTrue(Arrays.deepEquals(f9, o.f9));
419             TestCase.assertTrue(Arrays.deepEquals(f10, o.f10));
420             TestCase.assertTrue(Arrays.deepEquals(f11, o.f11));
421         }
422     }
423 
424     @Test
testEnumTypes()425     public void testEnumTypes()
426         throws FileNotFoundException, DatabaseException {
427 
428         open();
429 
430         checkEntity(EnumTypes.class, new EnumTypes());
431 
432         checkMetadata(EnumTypes.class.getName(), new String[][] {
433                           {"f0", "int"},
434                           {"f1", Thread.State.class.getName()},
435                           {"f2", MyEnum.class.getName()},
436                           {"f3", Object.class.getName()},
437                           {"f4", MyEnumCSM.class.getName()},
438                           {"f5", Object.class.getName()},
439                       },
440                       0 /*priKeyIndex*/, null);
441 
442         close();
443     }
444 
445     enum MyEnum { ONE, TWO };
446 
447     enum MyEnumCSM {
448 
449         A {
Foo()450             void Foo() {
451                 System.out.println("This is A!");
452             }
453         },
454 
B(true)455         B(true) {
456             void Foo() {
457                 System.out.println("This is B!");
458             }
459         };
460 
461         private boolean b;
462 
MyEnumCSM()463         MyEnumCSM() {
464             this(false);
465         }
466 
MyEnumCSM(boolean b)467         MyEnumCSM(boolean b) {
468             this.b = b;
469         }
470 
Foo()471         abstract void Foo();
472     };
473 
474     @Entity
475     static class EnumTypes implements MyEntity {
476 
477         @PrimaryKey
478         private final int f0 = 1;
479         private final Thread.State f1 = Thread.State.RUNNABLE;
480         private final MyEnum f2 = MyEnum.ONE;
481         private final Object f3 = MyEnum.TWO;
482         private final MyEnumCSM f4 = MyEnumCSM.A;
483         private final Object f5 = MyEnumCSM.B;
484 
EnumTypes()485         EnumTypes() { }
486 
getPriKeyObject()487         public Object getPriKeyObject() {
488             return f0;
489         }
490 
validate(Object other)491         public void validate(Object other) {
492             EnumTypes o = (EnumTypes) other;
493             TestCase.assertEquals(f0, o.f0);
494             TestCase.assertSame(f1, o.f1);
495             TestCase.assertSame(f2, o.f2);
496             TestCase.assertSame(f3, o.f3);
497             TestCase.assertSame(f4, o.f4);
498             TestCase.assertSame(f5, o.f5);
499         }
500     }
501 
502     @Test
testEnumObjectTypes()503     public void testEnumObjectTypes()
504         throws FileNotFoundException, DatabaseException {
505 
506         open();
507 
508         checkEntity(EnumObjectTypes.class, new EnumObjectTypes());
509 
510         checkMetadata(EnumObjectTypes.class.getName(), new String[][] {
511                           {"f0", "int"},
512                           {"f1", Object.class.getName()},
513                           {"f2", Object.class.getName()},
514                       },
515                       0 /*priKeyIndex*/, null);
516 
517         close();
518     }
519 
520     @Entity
521     static class EnumObjectTypes implements MyEntity {
522 
523         @PrimaryKey
524         private final int f0 = 1;
525         private final Object f1 = MyEnum.ONE;
526         private final Object f2 = MyEnumCSM.A;
527 
EnumObjectTypes()528         EnumObjectTypes() { }
529 
getPriKeyObject()530         public Object getPriKeyObject() {
531             return f0;
532         }
533 
validate(Object other)534         public void validate(Object other) {
535             EnumObjectTypes o = (EnumObjectTypes) other;
536             TestCase.assertEquals(f0, o.f0);
537             TestCase.assertSame(f1, o.f1);
538             TestCase.assertSame(f2, o.f2);
539         }
540     }
541 
542     @Test
testProxyTypes()543     public void testProxyTypes()
544         throws FileNotFoundException, DatabaseException {
545 
546         open();
547 
548         checkEntity(ProxyTypes.class, new ProxyTypes());
549 
550         checkMetadata(ProxyTypes.class.getName(), new String[][] {
551                           {"f0", "int"},
552                           {"f1", Locale.class.getName()},
553                           {"f2", Set.class.getName()},
554                           {"f3", Set.class.getName()},
555                           {"f4", Object.class.getName()},
556                           {"f5", HashMap.class.getName()},
557                           {"f6", TreeMap.class.getName()},
558                           {"f7", List.class.getName()},
559                           {"f8", LinkedList.class.getName()},
560                           {"f9", LocalizedText.class.getName()},
561                           {"f10", LinkedHashMap.class.getName()},
562                       },
563                       0 /*priKeyIndex*/, null);
564 
565         close();
566     }
567 
568     @Entity
569     static class ProxyTypes implements MyEntity {
570 
571         @PrimaryKey
572         private final int f0 = 1;
573         private final Locale f1 = Locale.getDefault();
574         private final Set<Integer> f2 = new HashSet<Integer>();
575         private final Set<Integer> f3 = new TreeSet<Integer>();
576         private final Object f4 = new HashSet<Address>();
577         private final HashMap<String, Integer> f5 =
578             new HashMap<String, Integer>();
579         private final TreeMap<String, Address> f6 =
580             new TreeMap<String, Address>();
581         private final List<Integer> f7 = new ArrayList<Integer>();
582         private final LinkedList<Integer> f8 = new LinkedList<Integer>();
583         private final LocalizedText f9 = new LocalizedText(f1, "xyz");
584         private final LinkedHashMap<String, Integer> f10 =
585             new LinkedHashMap<String, Integer>();
586 
ProxyTypes()587         ProxyTypes() {
588             f2.add(123);
589             f2.add(456);
590             f3.add(456);
591             f3.add(123);
592             HashSet<Address> s = (HashSet) f4;
593             s.add(new Address("city", "state", 11111));
594             s.add(new Address("city2", "state2", 22222));
595             s.add(new Address("city3", "state3", 33333));
596             f5.put("one", 111);
597             f5.put("two", 222);
598             f5.put("three", 333);
599             f6.put("one", new Address("city", "state", 11111));
600             f6.put("two", new Address("city2", "state2", 22222));
601             f6.put("three", new Address("city3", "state3", 33333));
602             f7.add(123);
603             f7.add(456);
604             f8.add(123);
605             f8.add(456);
606             f10.put("one", 111);
607             f10.put("two", 222);
608             f10.put("three", 333);
609         }
610 
getPriKeyObject()611         public Object getPriKeyObject() {
612             return f0;
613         }
614 
validate(Object other)615         public void validate(Object other) {
616             ProxyTypes o = (ProxyTypes) other;
617             TestCase.assertEquals(f0, o.f0);
618             TestCase.assertEquals(f1, o.f1);
619             TestCase.assertEquals(f2, o.f2);
620             TestCase.assertEquals(f3, o.f3);
621             TestCase.assertEquals(f4, o.f4);
622             TestCase.assertEquals(f5, o.f5);
623             TestCase.assertEquals(f6, o.f6);
624             TestCase.assertEquals(f7, o.f7);
625             TestCase.assertEquals(f8, o.f8);
626             TestCase.assertEquals(f9, o.f9);
627             TestCase.assertEquals(f10, o.f10);
628         }
629     }
630 
631     @Persistent(proxyFor=Locale.class)
632     static class LocaleProxy implements PersistentProxy<Locale> {
633 
634         String language;
635         String country;
636         String variant;
637 
LocaleProxy()638         private LocaleProxy() {}
639 
initializeProxy(Locale object)640         public void initializeProxy(Locale object) {
641             language = object.getLanguage();
642             country = object.getCountry();
643             variant = object.getVariant();
644         }
645 
convertProxy()646         public Locale convertProxy() {
647             return new Locale(language, country, variant);
648         }
649     }
650 
651     static class LocalizedText {
652 
653         Locale locale;
654         String text;
655 
LocalizedText(Locale locale, String text)656         LocalizedText(Locale locale, String text) {
657             this.locale = locale;
658             this.text = text;
659         }
660 
661         @Override
equals(Object other)662         public boolean equals(Object other) {
663             LocalizedText o = (LocalizedText) other;
664             return text.equals(o.text) &&
665                    locale.equals(o.locale);
666         }
667     }
668 
669     @Persistent(proxyFor=LocalizedText.class)
670     static class LocalizedTextProxy implements PersistentProxy<LocalizedText> {
671 
672         Locale locale;
673         String text;
674 
LocalizedTextProxy()675         private LocalizedTextProxy() {}
676 
initializeProxy(LocalizedText object)677         public void initializeProxy(LocalizedText object) {
678             locale = object.locale;
679             text = object.text;
680         }
681 
convertProxy()682         public LocalizedText convertProxy() {
683             return new LocalizedText(locale, text);
684         }
685     }
686 
687     @Test
testEmbedded()688     public void testEmbedded()
689         throws FileNotFoundException, DatabaseException {
690 
691         open();
692 
693         Address a1 = new Address("city", "state", 123);
694         Address a2 = new Address("Wikieup", "AZ", 85360);
695 
696         checkEntity(Embedded.class,
697                     new Embedded("x", a1, a2));
698         checkEntity(Embedded.class,
699                     new Embedded("y", a1, null));
700         checkEntity(Embedded.class,
701                     new Embedded("", a2, a2));
702 
703         checkMetadata(Embedded.class.getName(), new String[][] {
704                         {"id", "java.lang.String"},
705                         {"idShadow", "java.lang.String"},
706                         {"one", Address.class.getName()},
707                         {"two", Address.class.getName()},
708                       },
709                       0 /*priKeyIndex*/, null);
710 
711         checkMetadata(Address.class.getName(), new String[][] {
712                         {"street", "java.lang.String"},
713                         {"city", "java.lang.String"},
714                         {"zip", "int"},
715                       },
716                       -1 /*priKeyIndex*/, null);
717 
718         close();
719     }
720 
721     @Entity
722     static class Embedded implements MyEntity {
723 
724         @PrimaryKey
725         private String id;
726         private String idShadow;
727         private Address one;
728         private Address two;
729 
Embedded()730         private Embedded() { }
731 
Embedded(String id, Address one, Address two)732         private Embedded(String id, Address one, Address two) {
733             this.id = id;
734             idShadow = id;
735             this.one = one;
736             this.two = two;
737         }
738 
getPriKeyObject()739         public Object getPriKeyObject() {
740             return id;
741         }
742 
validate(Object other)743         public void validate(Object other) {
744             Embedded o = (Embedded) other;
745             TestCase.assertEquals(id, o.id);
746             if (one != null) {
747                 one.validate(o.one);
748             } else {
749                 assertNull(o.one);
750             }
751             if (two != null) {
752                 two.validate(o.two);
753             } else {
754                 assertNull(o.two);
755             }
756             if (one == two) {
757                 TestCase.assertSame(o.one, o.two);
758             }
759         }
760 
761         @Override
toString()762         public String toString() {
763             return "" + id + ' ' + one + ' ' + two;
764         }
765     }
766 
767     @Persistent
768     static class Address {
769 
770         private String street;
771         private String city;
772         private int zip;
773 
Address()774         private Address() {}
775 
Address(String street, String city, int zip)776         Address(String street, String city, int zip) {
777             this.street = street;
778             this.city = city;
779             this.zip = zip;
780         }
781 
validate(Address o)782         void validate(Address o) {
783             TestCase.assertTrue(nullOrEqual(street, o.street));
784             TestCase.assertTrue(nullOrEqual(city, o.city));
785             TestCase.assertEquals(zip, o.zip);
786         }
787 
788         @Override
toString()789         public String toString() {
790             return "" + street + ' ' + city + ' ' + zip;
791         }
792 
793         @Override
equals(Object other)794         public boolean equals(Object other) {
795             if (other == null) {
796                 return false;
797             }
798             Address o = (Address) other;
799             return nullOrEqual(street, o.street) &&
800                    nullOrEqual(city, o.city) &&
801                    nullOrEqual(zip, o.zip);
802         }
803 
804         @Override
hashCode()805         public int hashCode() {
806             return zip;
807         }
808     }
809 
810     @Test
testSubclass()811     public void testSubclass()
812         throws FileNotFoundException, DatabaseException {
813 
814         open();
815 
816         checkEntity(Basic.class,
817                     new Subclass(-1, "xxx", -2, "xxx", "xxx", true));
818 
819         checkMetadata(Basic.class.getName(), new String[][] {
820                           {"id", "long"},
821                           {"one", "java.lang.String"},
822                           {"two", "double"},
823                           {"three", "java.lang.String"},
824                       },
825                       0 /*priKeyIndex*/, null);
826         checkMetadata(Subclass.class.getName(), new String[][] {
827                           {"one", "java.lang.String"},
828                           {"two", "boolean"},
829                       },
830                       -1 /*priKeyIndex*/, Basic.class.getName());
831 
832         close();
833     }
834 
835     @Persistent
836     static class Subclass extends Basic {
837 
838         private String one;
839         private boolean two;
840 
Subclass()841         private Subclass() {
842         }
843 
Subclass(long id, String one, double two, String three, String subOne, boolean subTwo)844         private Subclass(long id, String one, double two, String three,
845                          String subOne, boolean subTwo) {
846             super(id, one, two, three);
847             this.one = subOne;
848             this.two = subTwo;
849         }
850 
851         @Override
validate(Object other)852         public void validate(Object other) {
853             super.validate(other);
854             Subclass o = (Subclass) other;
855             TestCase.assertTrue(nullOrEqual(one, o.one));
856             TestCase.assertEquals(two, o.two);
857         }
858     }
859 
860     @Test
testSuperclass()861     public void testSuperclass()
862         throws FileNotFoundException, DatabaseException {
863 
864         open();
865 
866         checkEntity(UseSuperclass.class,
867                     new UseSuperclass(33, "xxx"));
868 
869         checkMetadata(Superclass.class.getName(), new String[][] {
870                           {"id", "int"},
871                           {"one", "java.lang.String"},
872                       },
873                       0 /*priKeyIndex*/, null);
874         checkMetadata(UseSuperclass.class.getName(), new String[][] {
875                       },
876                       -1 /*priKeyIndex*/, Superclass.class.getName());
877 
878         close();
879     }
880 
881     @Persistent
882     static class Superclass implements MyEntity {
883 
884         @PrimaryKey
885         private int id;
886         private String one;
887 
Superclass()888         private Superclass() { }
889 
Superclass(int id, String one)890         private Superclass(int id, String one) {
891             this.id = id;
892             this.one = one;
893         }
894 
getPriKeyObject()895         public Object getPriKeyObject() {
896             return id;
897         }
898 
validate(Object other)899         public void validate(Object other) {
900             Superclass o = (Superclass) other;
901             TestCase.assertEquals(id, o.id);
902             TestCase.assertTrue(nullOrEqual(one, o.one));
903         }
904     }
905 
906     @Entity
907     static class UseSuperclass extends Superclass {
908 
UseSuperclass()909         private UseSuperclass() { }
910 
UseSuperclass(int id, String one)911         private UseSuperclass(int id, String one) {
912             super(id, one);
913         }
914     }
915 
916     @Test
testAbstract()917     public void testAbstract()
918         throws FileNotFoundException, DatabaseException {
919 
920         open();
921 
922         checkEntity(EntityUseAbstract.class,
923                     new EntityUseAbstract(33, "xxx"));
924 
925         checkMetadata(Abstract.class.getName(), new String[][] {
926                           {"one", "java.lang.String"},
927                       },
928                       -1 /*priKeyIndex*/, null);
929         checkMetadata(EmbeddedUseAbstract.class.getName(), new String[][] {
930                           {"two", "java.lang.String"},
931                       },
932                       -1 /*priKeyIndex*/, Abstract.class.getName());
933         checkMetadata(EntityUseAbstract.class.getName(), new String[][] {
934                           {"id", "int"},
935                           {"f1", EmbeddedUseAbstract.class.getName()},
936                           {"f2", Abstract.class.getName()},
937                           {"f3", Object.class.getName()},
938                           {"f4", Interface.class.getName()},
939                           {"a1", EmbeddedUseAbstract[].class.getName()},
940                           {"a2", Abstract[].class.getName()},
941                           {"a3", Abstract[].class.getName()},
942                           {"a4", Object[].class.getName()},
943                           {"a5", Interface[].class.getName()},
944                           {"a6", Interface[].class.getName()},
945                           {"a7", Interface[].class.getName()},
946                       },
947                       0 /*priKeyIndex*/, Abstract.class.getName());
948 
949         close();
950     }
951 
952     @Persistent
953     static abstract class Abstract implements Interface {
954 
955         String one;
956 
Abstract()957         private Abstract() { }
958 
Abstract(String one)959         private Abstract(String one) {
960             this.one = one;
961         }
962 
validate(Object other)963         public void validate(Object other) {
964             Abstract o = (Abstract) other;
965             TestCase.assertTrue(nullOrEqual(one, o.one));
966         }
967 
968         @Override
equals(Object other)969         public boolean equals(Object other) {
970             Abstract o = (Abstract) other;
971             return nullOrEqual(one, o.one);
972         }
973     }
974 
975     interface Interface {
validate(Object other)976         void validate(Object other);
977     }
978 
979     @Persistent
980     static class EmbeddedUseAbstract extends Abstract {
981 
982         private String two;
983 
EmbeddedUseAbstract()984         private EmbeddedUseAbstract() { }
985 
EmbeddedUseAbstract(String one, String two)986         private EmbeddedUseAbstract(String one, String two) {
987             super(one);
988             this.two = two;
989         }
990 
991         @Override
validate(Object other)992         public void validate(Object other) {
993             super.validate(other);
994             EmbeddedUseAbstract o = (EmbeddedUseAbstract) other;
995             TestCase.assertTrue(nullOrEqual(two, o.two));
996         }
997 
998         @Override
equals(Object other)999         public boolean equals(Object other) {
1000             if (!super.equals(other)) {
1001                 return false;
1002             }
1003             EmbeddedUseAbstract o = (EmbeddedUseAbstract) other;
1004             return nullOrEqual(two, o.two);
1005         }
1006     }
1007 
1008     @Entity
1009     static class EntityUseAbstract extends Abstract implements MyEntity {
1010 
1011         @PrimaryKey
1012         private int id;
1013 
1014         private EmbeddedUseAbstract f1;
1015         private Abstract f2;
1016         private Object f3;
1017         private Interface f4;
1018         private EmbeddedUseAbstract[] a1;
1019         private Abstract[] a2;
1020         private Abstract[] a3;
1021         private Object[] a4;
1022         private Interface[] a5;
1023         private Interface[] a6;
1024         private Interface[] a7;
1025 
EntityUseAbstract()1026         private EntityUseAbstract() { }
1027 
EntityUseAbstract(int id, String one)1028         private EntityUseAbstract(int id, String one) {
1029             super(one);
1030             this.id = id;
1031             f1 = new EmbeddedUseAbstract(one, one);
1032             f2 = new EmbeddedUseAbstract(one + "x", one + "y");
1033             f3 = new EmbeddedUseAbstract(null, null);
1034             f4 = new EmbeddedUseAbstract(null, null);
1035             a1 = new EmbeddedUseAbstract[3];
1036             a2 = new EmbeddedUseAbstract[3];
1037             a3 = new Abstract[3];
1038             a4 = new Object[3];
1039             a5 = new EmbeddedUseAbstract[3];
1040             a6 = new Abstract[3];
1041             a7 = new Interface[3];
1042             for (int i = 0; i < 3; i += 1) {
1043                 a1[i] = new EmbeddedUseAbstract("1" + i, null);
1044                 a2[i] = new EmbeddedUseAbstract("2" + i, null);
1045                 a3[i] = new EmbeddedUseAbstract("3" + i, null);
1046                 a4[i] = new EmbeddedUseAbstract("4" + i, null);
1047                 a5[i] = new EmbeddedUseAbstract("5" + i, null);
1048                 a6[i] = new EmbeddedUseAbstract("6" + i, null);
1049                 a7[i] = new EmbeddedUseAbstract("7" + i, null);
1050             }
1051         }
1052 
getPriKeyObject()1053         public Object getPriKeyObject() {
1054             return id;
1055         }
1056 
1057         @Override
validate(Object other)1058         public void validate(Object other) {
1059             super.validate(other);
1060             EntityUseAbstract o = (EntityUseAbstract) other;
1061             TestCase.assertEquals(id, o.id);
1062             f1.validate(o.f1);
1063             f2.validate(o.f2);
1064             ((Abstract) f3).validate(o.f3);
1065             f4.validate(o.f4);
1066             assertTrue(arrayToString(a1) + ' ' + arrayToString(o.a1),
1067                        Arrays.equals(a1, o.a1));
1068             assertTrue(Arrays.equals(a2, o.a2));
1069             assertTrue(Arrays.equals(a3, o.a3));
1070             assertTrue(Arrays.equals(a4, o.a4));
1071             assertTrue(Arrays.equals(a5, o.a5));
1072             assertTrue(Arrays.equals(a6, o.a6));
1073             assertTrue(Arrays.equals(a7, o.a7));
1074             assertSame(EmbeddedUseAbstract.class, f2.getClass());
1075             assertSame(EmbeddedUseAbstract.class, f3.getClass());
1076             assertSame(EmbeddedUseAbstract[].class, a1.getClass());
1077             assertSame(EmbeddedUseAbstract[].class, a2.getClass());
1078             assertSame(Abstract[].class, a3.getClass());
1079             assertSame(Object[].class, a4.getClass());
1080             assertSame(EmbeddedUseAbstract[].class, a5.getClass());
1081             assertSame(Abstract[].class, a6.getClass());
1082             assertSame(Interface[].class, a7.getClass());
1083         }
1084     }
1085 
1086     @Test
testCompositeKey()1087     public void testCompositeKey()
1088         throws FileNotFoundException, DatabaseException {
1089 
1090         open();
1091 
1092         CompositeKey key =
1093             new CompositeKey(123, 456L, "xyz", BigInteger.valueOf(789),
1094                              MyEnum.ONE, MyEnumCSM.A,
1095                              BigDecimal.valueOf(123.123));
1096         checkEntity(UseCompositeKey.class,
1097                     new UseCompositeKey(key, "one"));
1098 
1099         checkMetadata(UseCompositeKey.class.getName(), new String[][] {
1100                           {"key", CompositeKey.class.getName()},
1101                           {"one", "java.lang.String"},
1102                       },
1103                       0 /*priKeyIndex*/, null);
1104 
1105         checkMetadata(CompositeKey.class.getName(), new String[][] {
1106                         {"f1", "int"},
1107                         {"f2", "java.lang.Long"},
1108                         {"f3", "java.lang.String"},
1109                         {"f4", "java.math.BigInteger"},
1110                         {"f5", MyEnum.class.getName()},
1111                         {"f6", MyEnumCSM.class.getName()},
1112                         {"f7", BigDecimal.class.getName()},
1113                       },
1114                       -1 /*priKeyIndex*/, null);
1115 
1116         close();
1117     }
1118 
1119     @Persistent
1120     static class CompositeKey {
1121         @KeyField(3)
1122         private int f1;
1123         @KeyField(2)
1124         private Long f2;
1125         @KeyField(1)
1126         private String f3;
1127         @KeyField(4)
1128         private BigInteger f4;
1129         @KeyField(5)
1130         private MyEnum f5;
1131         @KeyField(6)
1132         private MyEnumCSM f6;
1133         @KeyField(7)
1134         private BigDecimal f7;
1135 
CompositeKey()1136         private CompositeKey() {}
1137 
CompositeKey(int f1, Long f2, String f3, BigInteger f4, MyEnum f5, MyEnumCSM f6, BigDecimal f7)1138         CompositeKey(int f1,
1139                      Long f2,
1140                      String f3,
1141                      BigInteger f4,
1142                      MyEnum f5,
1143                      MyEnumCSM f6,
1144                      BigDecimal f7) {
1145             this.f1 = f1;
1146             this.f2 = f2;
1147             this.f3 = f3;
1148             this.f4 = f4;
1149             this.f5 = f5;
1150             this.f6 = f6;
1151             this.f7 = f7;
1152         }
1153 
validate(CompositeKey o)1154         void validate(CompositeKey o) {
1155             TestCase.assertEquals(f1, o.f1);
1156             TestCase.assertTrue(nullOrEqual(f2, o.f2));
1157             TestCase.assertTrue(nullOrEqual(f3, o.f3));
1158             TestCase.assertTrue(nullOrEqual(f4, o.f4));
1159             TestCase.assertEquals(f5, o.f5);
1160             TestCase.assertTrue(nullOrEqual(f5, o.f5));
1161             TestCase.assertEquals(f5, o.f5);
1162             TestCase.assertTrue(nullOrEqual(f6, o.f6));
1163             TestCase.assertTrue(nullOrEqual(f7, o.f7));
1164         }
1165 
1166         @Override
equals(Object other)1167         public boolean equals(Object other) {
1168             CompositeKey o = (CompositeKey) other;
1169             return f1 == o.f1 &&
1170                    nullOrEqual(f2, o.f2) &&
1171                    nullOrEqual(f3, o.f3) &&
1172                    nullOrEqual(f4, o.f4) &&
1173                    nullOrEqual(f5, o.f5) &&
1174                    nullOrEqual(f6, o.f6) &&
1175                    nullOrEqual(f7, o.f7);
1176         }
1177 
1178         @Override
hashCode()1179         public int hashCode() {
1180             return f1;
1181         }
1182 
1183         @Override
toString()1184         public String toString() {
1185             return "" + f1 + ' ' + f2 + ' ' + f3 + ' ' + f4 + ' ' + f5 + ' ' +
1186                    f6 + ' ' + f7;
1187         }
1188     }
1189 
1190     @Entity
1191     static class UseCompositeKey implements MyEntity {
1192 
1193         @PrimaryKey
1194         private CompositeKey key;
1195         private String one;
1196 
UseCompositeKey()1197         private UseCompositeKey() { }
1198 
UseCompositeKey(CompositeKey key, String one)1199         private UseCompositeKey(CompositeKey key, String one) {
1200             this.key = key;
1201             this.one = one;
1202         }
1203 
getPriKeyObject()1204         public Object getPriKeyObject() {
1205             return key;
1206         }
1207 
validate(Object other)1208         public void validate(Object other) {
1209             UseCompositeKey o = (UseCompositeKey) other;
1210             TestCase.assertNotNull(key);
1211             TestCase.assertNotNull(o.key);
1212             key.validate(o.key);
1213             TestCase.assertTrue(nullOrEqual(one, o.one));
1214         }
1215     }
1216 
1217     @Test
testComparableKey()1218     public void testComparableKey()
1219         throws FileNotFoundException, DatabaseException {
1220 
1221         open();
1222 
1223         ComparableKey key = new ComparableKey(123, 456);
1224         checkEntity(UseComparableKey.class,
1225                     new UseComparableKey(key, "one"));
1226 
1227         checkMetadata(UseComparableKey.class.getName(), new String[][] {
1228                           {"key", ComparableKey.class.getName()},
1229                           {"one", "java.lang.String"},
1230                       },
1231                       0 /*priKeyIndex*/, null);
1232 
1233         checkMetadata(ComparableKey.class.getName(), new String[][] {
1234                         {"f1", "int"},
1235                         {"f2", "int"},
1236                       },
1237                       -1 /*priKeyIndex*/, null);
1238 
1239         ClassMetadata classMeta =
1240             model.getClassMetadata(UseComparableKey.class.getName());
1241         assertNotNull(classMeta);
1242 
1243         PersistKeyBinding binding = new PersistKeyBinding
1244             (catalog, ComparableKey.class.getName(), false);
1245 
1246         PersistComparator comparator = new PersistComparator(binding);
1247 
1248         compareKeys(comparator, binding, new ComparableKey(1, 1),
1249                                          new ComparableKey(1, 1), 0);
1250         compareKeys(comparator, binding, new ComparableKey(1, 2),
1251                                          new ComparableKey(1, 1), -1);
1252         compareKeys(comparator, binding, new ComparableKey(2, 1),
1253                                          new ComparableKey(1, 1), -1);
1254         compareKeys(comparator, binding, new ComparableKey(2, 1),
1255                                          new ComparableKey(3, 1), 1);
1256 
1257         close();
1258     }
1259 
compareKeys(Comparator<byte[]> comparator, EntryBinding binding, Object key1, Object key2, int expectResult)1260     private void compareKeys(Comparator<byte[]> comparator,
1261                              EntryBinding binding,
1262                              Object key1,
1263                              Object key2,
1264                              int expectResult) {
1265         DatabaseEntry entry1 = new DatabaseEntry();
1266         DatabaseEntry entry2 = new DatabaseEntry();
1267         binding.objectToEntry(key1, entry1);
1268         binding.objectToEntry(key2, entry2);
1269         int result = comparator.compare(entry1.getData(), entry2.getData());
1270         assertEquals(expectResult, result);
1271     }
1272 
1273     @Persistent
1274     static class ComparableKey implements Comparable<ComparableKey> {
1275         @KeyField(2)
1276         private int f1;
1277         @KeyField(1)
1278         private int f2;
1279 
ComparableKey()1280         private ComparableKey() {}
1281 
ComparableKey(int f1, int f2)1282         ComparableKey(int f1, int f2) {
1283             this.f1 = f1;
1284             this.f2 = f2;
1285         }
1286 
validate(ComparableKey o)1287         void validate(ComparableKey o) {
1288             TestCase.assertEquals(f1, o.f1);
1289             TestCase.assertEquals(f2, o.f2);
1290         }
1291 
1292         @Override
equals(Object other)1293         public boolean equals(Object other) {
1294             ComparableKey o = (ComparableKey) other;
1295             return f1 == o.f1 && f2 == o.f2;
1296         }
1297 
1298         @Override
hashCode()1299         public int hashCode() {
1300             return f1 + f2;
1301         }
1302 
1303         @Override
toString()1304         public String toString() {
1305             return "" + f1 + ' ' + f2;
1306         }
1307 
1308         /** Compare f1 then f2, in reverse integer order. */
compareTo(ComparableKey o)1309         public int compareTo(ComparableKey o) {
1310             if (f1 != o.f1) {
1311                 return o.f1 - f1;
1312             } else {
1313                 return o.f2 - f2;
1314             }
1315         }
1316     }
1317 
1318     @Entity
1319     static class UseComparableKey implements MyEntity {
1320 
1321         @PrimaryKey
1322         private ComparableKey key;
1323         private String one;
1324 
UseComparableKey()1325         private UseComparableKey() { }
1326 
UseComparableKey(ComparableKey key, String one)1327         private UseComparableKey(ComparableKey key, String one) {
1328             this.key = key;
1329             this.one = one;
1330         }
1331 
getPriKeyObject()1332         public Object getPriKeyObject() {
1333             return key;
1334         }
1335 
validate(Object other)1336         public void validate(Object other) {
1337             UseComparableKey o = (UseComparableKey) other;
1338             TestCase.assertNotNull(key);
1339             TestCase.assertNotNull(o.key);
1340             key.validate(o.key);
1341             TestCase.assertTrue(nullOrEqual(one, o.one));
1342         }
1343     }
1344 
1345     @Test
testSecKeys()1346     public void testSecKeys()
1347         throws FileNotFoundException, DatabaseException {
1348 
1349         open();
1350 
1351         SecKeys obj = new SecKeys();
1352         checkEntity(SecKeys.class, obj);
1353 
1354         checkMetadata(SecKeys.class.getName(), new String[][] {
1355                           {"id", "long"},
1356                           {"f0", "boolean"},
1357                           {"g0", "boolean"},
1358                           {"f1", "char"},
1359                           {"g1", "char"},
1360                           {"f2", "byte"},
1361                           {"g2", "byte"},
1362                           {"f3", "short"},
1363                           {"g3", "short"},
1364                           {"f4", "int"},
1365                           {"g4", "int"},
1366                           {"f5", "long"},
1367                           {"g5", "long"},
1368                           {"f6", "float"},
1369                           {"g6", "float"},
1370                           {"f7", "double"},
1371                           {"g7", "double"},
1372                           {"f8", "java.lang.String"},
1373                           {"g8", "java.lang.String"},
1374                           {"f9", "java.math.BigInteger"},
1375                           {"g9", "java.math.BigInteger"},
1376                           {"f10", "java.math.BigDecimal"},
1377                           {"g10", "java.math.BigDecimal"},
1378                           {"f11", "java.util.Date"},
1379                           {"g11", "java.util.Date"},
1380                           {"f12", "java.lang.Boolean"},
1381                           {"g12", "java.lang.Boolean"},
1382                           {"f13", "java.lang.Character"},
1383                           {"g13", "java.lang.Character"},
1384                           {"f14", "java.lang.Byte"},
1385                           {"g14", "java.lang.Byte"},
1386                           {"f15", "java.lang.Short"},
1387                           {"g15", "java.lang.Short"},
1388                           {"f16", "java.lang.Integer"},
1389                           {"g16", "java.lang.Integer"},
1390                           {"f17", "java.lang.Long"},
1391                           {"g17", "java.lang.Long"},
1392                           {"f18", "java.lang.Float"},
1393                           {"g18", "java.lang.Float"},
1394                           {"f19", "java.lang.Double"},
1395                           {"g19", "java.lang.Double"},
1396                           {"f20", CompositeKey.class.getName()},
1397                           {"g20", CompositeKey.class.getName()},
1398                           {"f21", int[].class.getName()},
1399                           {"g21", int[].class.getName()},
1400                           {"f22", Integer[].class.getName()},
1401                           {"g22", Integer[].class.getName()},
1402                           {"f23", Set.class.getName()},
1403                           {"g23", Set.class.getName()},
1404                           {"f24", CompositeKey[].class.getName()},
1405                           {"g24", CompositeKey[].class.getName()},
1406                           {"f25", Set.class.getName()},
1407                           {"g25", Set.class.getName()},
1408                           {"f26", MyEnum.class.getName()},
1409                           {"g26", MyEnum.class.getName()},
1410                           {"f27", MyEnum[].class.getName()},
1411                           {"g27", MyEnum[].class.getName()},
1412                           {"f28", Set.class.getName()},
1413                           {"g28", Set.class.getName()},
1414                           {"f31", "java.util.Date"},
1415                           {"f32", "java.lang.Boolean"},
1416                           {"f33", "java.lang.Character"},
1417                           {"f34", "java.lang.Byte"},
1418                           {"f35", "java.lang.Short"},
1419                           {"f36", "java.lang.Integer"},
1420                           {"f37", "java.lang.Long"},
1421                           {"f38", "java.lang.Float"},
1422                           {"f39", "java.lang.Double"},
1423                           {"f40", CompositeKey.class.getName()},
1424                           {"f41", MyEnumCSM.class.getName()},
1425                           {"g41", MyEnumCSM.class.getName()},
1426                           {"f42", MyEnumCSM[].class.getName()},
1427                           {"g42", MyEnumCSM[].class.getName()},
1428                           {"f43", Set.class.getName()},
1429                           {"g43", Set.class.getName()},
1430                       },
1431                       0 /*priKeyIndex*/, null);
1432 
1433         checkSecKey(obj, "f0", obj.f0, Boolean.class);
1434         checkSecKey(obj, "f1", obj.f1, Character.class);
1435         checkSecKey(obj, "f2", obj.f2, Byte.class);
1436         checkSecKey(obj, "f3", obj.f3, Short.class);
1437         checkSecKey(obj, "f4", obj.f4, Integer.class);
1438         checkSecKey(obj, "f5", obj.f5, Long.class);
1439         checkSecKey(obj, "f6", obj.f6, Float.class);
1440         checkSecKey(obj, "f7", obj.f7, Double.class);
1441         checkSecKey(obj, "f8", obj.f8, String.class);
1442         checkSecKey(obj, "f9", obj.f9, BigInteger.class);
1443         checkSecKey(obj, "f10", obj.f10, BigDecimal.class);
1444         checkSecKey(obj, "f11", obj.f11, Date.class);
1445         checkSecKey(obj, "f12", obj.f12, Boolean.class);
1446         checkSecKey(obj, "f13", obj.f13, Character.class);
1447         checkSecKey(obj, "f14", obj.f14, Byte.class);
1448         checkSecKey(obj, "f15", obj.f15, Short.class);
1449         checkSecKey(obj, "f16", obj.f16, Integer.class);
1450         checkSecKey(obj, "f17", obj.f17, Long.class);
1451         checkSecKey(obj, "f18", obj.f18, Float.class);
1452         checkSecKey(obj, "f19", obj.f19, Double.class);
1453         checkSecKey(obj, "f20", obj.f20, CompositeKey.class);
1454         checkSecKey(obj, "f26", obj.f26, MyEnum.class);
1455         checkSecKey(obj, "f41", obj.f41, MyEnumCSM.class);
1456 
1457         checkSecMultiKey(obj, "f21", toSet(obj.f21), Integer.class);
1458         checkSecMultiKey(obj, "f22", toSet(obj.f22), Integer.class);
1459         checkSecMultiKey(obj, "f23", toSet(obj.f23), Integer.class);
1460         checkSecMultiKey(obj, "f24", toSet(obj.f24), CompositeKey.class);
1461         checkSecMultiKey(obj, "f25", toSet(obj.f25), CompositeKey.class);
1462         checkSecMultiKey(obj, "f27", toSet(obj.f27), MyEnum.class);
1463         checkSecMultiKey(obj, "f28", toSet(obj.f28), MyEnum.class);
1464         checkSecMultiKey(obj, "f42", toSet(obj.f42), MyEnumCSM.class);
1465         checkSecMultiKey(obj, "f43", toSet(obj.f43), MyEnumCSM.class);
1466 
1467         nullifySecKey(obj, "f8", obj.f8, String.class);
1468         nullifySecKey(obj, "f9", obj.f9, BigInteger.class);
1469         nullifySecKey(obj, "f10", obj.f10, BigDecimal.class);
1470         nullifySecKey(obj, "f11", obj.f11, Date.class);
1471         nullifySecKey(obj, "f12", obj.f12, Boolean.class);
1472         nullifySecKey(obj, "f13", obj.f13, Character.class);
1473         nullifySecKey(obj, "f14", obj.f14, Byte.class);
1474         nullifySecKey(obj, "f15", obj.f15, Short.class);
1475         nullifySecKey(obj, "f16", obj.f16, Integer.class);
1476         nullifySecKey(obj, "f17", obj.f17, Long.class);
1477         nullifySecKey(obj, "f18", obj.f18, Float.class);
1478         nullifySecKey(obj, "f19", obj.f19, Double.class);
1479         nullifySecKey(obj, "f20", obj.f20, CompositeKey.class);
1480         nullifySecKey(obj, "f26", obj.f26, MyEnum.class);
1481         nullifySecKey(obj, "f41", obj.f41, MyEnumCSM.class);
1482 
1483         nullifySecMultiKey(obj, "f21", obj.f21, Integer.class);
1484         nullifySecMultiKey(obj, "f22", obj.f22, Integer.class);
1485         nullifySecMultiKey(obj, "f23", obj.f23, Integer.class);
1486         nullifySecMultiKey(obj, "f24", obj.f24, CompositeKey.class);
1487         nullifySecMultiKey(obj, "f25", obj.f25, CompositeKey.class);
1488         nullifySecMultiKey(obj, "f27", obj.f27, MyEnum.class);
1489         nullifySecMultiKey(obj, "f28", obj.f28, MyEnum.class);
1490         nullifySecMultiKey(obj, "f42", obj.f42, MyEnumCSM.class);
1491         nullifySecMultiKey(obj, "f43", obj.f43, MyEnumCSM.class);
1492 
1493         nullifySecKey(obj, "f31", obj.f31, Date.class);
1494         nullifySecKey(obj, "f32", obj.f32, Boolean.class);
1495         nullifySecKey(obj, "f33", obj.f33, Character.class);
1496         nullifySecKey(obj, "f34", obj.f34, Byte.class);
1497         nullifySecKey(obj, "f35", obj.f35, Short.class);
1498         nullifySecKey(obj, "f36", obj.f36, Integer.class);
1499         nullifySecKey(obj, "f37", obj.f37, Long.class);
1500         nullifySecKey(obj, "f38", obj.f38, Float.class);
1501         nullifySecKey(obj, "f39", obj.f39, Double.class);
1502         nullifySecKey(obj, "f40", obj.f40, CompositeKey.class);
1503 
1504         close();
1505     }
1506 
toSet(int[] a)1507     static Set toSet(int[] a) {
1508         Set set = new HashSet();
1509         for (int i : a) {
1510             set.add(i);
1511         }
1512         return set;
1513     }
1514 
toSet(Object[] a)1515     static Set toSet(Object[] a) {
1516         return new HashSet(Arrays.asList(a));
1517     }
1518 
toSet(Set s)1519     static Set toSet(Set s) {
1520         return s;
1521     }
1522 
1523     @Entity
1524     static class SecKeys implements MyEntity {
1525 
1526         @PrimaryKey
1527         long id;
1528 
1529         @SecondaryKey(relate=MANY_TO_ONE)
1530         private final boolean f0 = false;
1531         private final boolean g0 = false;
1532 
1533         @SecondaryKey(relate=MANY_TO_ONE)
1534         private final char f1 = '1';
1535         private final char g1 = '1';
1536 
1537         @SecondaryKey(relate=MANY_TO_ONE)
1538         private final byte f2 = 2;
1539         private final byte g2 = 2;
1540 
1541         @SecondaryKey(relate=MANY_TO_ONE)
1542         private final short f3 = 3;
1543         private final short g3 = 3;
1544 
1545         @SecondaryKey(relate=MANY_TO_ONE)
1546         private final int f4 = 4;
1547         private final int g4 = 4;
1548 
1549         @SecondaryKey(relate=MANY_TO_ONE)
1550         private final long f5 = 5;
1551         private final long g5 = 5;
1552 
1553         @SecondaryKey(relate=MANY_TO_ONE)
1554         private final float f6 = 6.6f;
1555         private final float g6 = 6.6f;
1556 
1557         @SecondaryKey(relate=MANY_TO_ONE)
1558         private final double f7 = 7.7;
1559         private final double g7 = 7.7;
1560 
1561         @SecondaryKey(relate=MANY_TO_ONE)
1562         private final String f8 = "8";
1563         private final String g8 = "8";
1564 
1565         @SecondaryKey(relate=MANY_TO_ONE)
1566         private BigInteger f9;
1567         private BigInteger g9;
1568 
1569         @SecondaryKey(relate=MANY_TO_ONE)
1570         private BigDecimal f10;
1571         private BigDecimal g10;
1572 
1573         @SecondaryKey(relate=MANY_TO_ONE)
1574         private final Date f11 = new Date(11);
1575         private final Date g11 = new Date(11);
1576 
1577         @SecondaryKey(relate=MANY_TO_ONE)
1578         private final Boolean f12 = true;
1579         private final Boolean g12 = true;
1580 
1581         @SecondaryKey(relate=MANY_TO_ONE)
1582         private final Character f13 = '3';
1583         private final Character g13 = '3';
1584 
1585         @SecondaryKey(relate=MANY_TO_ONE)
1586         private final Byte f14 = 14;
1587         private final Byte g14 = 14;
1588 
1589         @SecondaryKey(relate=MANY_TO_ONE)
1590         private final Short f15 = 15;
1591         private final Short g15 = 15;
1592 
1593         @SecondaryKey(relate=MANY_TO_ONE)
1594         private final Integer f16 = 16;
1595         private final Integer g16 = 16;
1596 
1597         @SecondaryKey(relate=MANY_TO_ONE)
1598         private final Long f17= 17L;
1599         private final Long g17= 17L;
1600 
1601         @SecondaryKey(relate=MANY_TO_ONE)
1602         private final Float f18 = 18.18f;
1603         private final Float g18 = 18.18f;
1604 
1605         @SecondaryKey(relate=MANY_TO_ONE)
1606         private final Double f19 = 19.19;
1607         private final Double g19 = 19.19;
1608 
1609         @SecondaryKey(relate=MANY_TO_ONE)
1610         private final CompositeKey f20 =
1611             new CompositeKey(20, 20L, "20", BigInteger.valueOf(20),
1612                              MyEnum.ONE, MyEnumCSM.A,
1613                              BigDecimal.valueOf(123.123));
1614         private final CompositeKey g20 =
1615             new CompositeKey(20, 20L, "20", BigInteger.valueOf(20),
1616                              MyEnum.TWO, MyEnumCSM.B,
1617                              BigDecimal.valueOf(123.123));
1618 
1619         private static int[] arrayOfInt = { 100, 101, 102 };
1620 
1621         private static Integer[] arrayOfInteger = { 100, 101, 102 };
1622 
1623         private static CompositeKey[] arrayOfCompositeKey = {
1624             new CompositeKey(100, 100L, "100", BigInteger.valueOf(100),
1625                              MyEnum.ONE, MyEnumCSM.A,
1626                              BigDecimal.valueOf(123.123)),
1627             new CompositeKey(101, 101L, "101", BigInteger.valueOf(101),
1628                              MyEnum.TWO, MyEnumCSM.B,
1629                              BigDecimal.valueOf(123.123)),
1630             new CompositeKey(102, 102L, "102", BigInteger.valueOf(102),
1631                              MyEnum.TWO, MyEnumCSM.B,
1632                              BigDecimal.valueOf(123.123)),
1633         };
1634 
1635         private static MyEnum[] arrayOfEnum =
1636             new MyEnum[] { MyEnum.ONE, MyEnum.TWO };
1637 
1638         private static MyEnumCSM[] arrayOfEnumCSM =
1639             new MyEnumCSM[] { MyEnumCSM.A, MyEnumCSM.B };
1640 
1641         @SecondaryKey(relate=ONE_TO_MANY)
1642         private final int[] f21 = arrayOfInt;
1643         private final int[] g21 = f21;
1644 
1645         @SecondaryKey(relate=ONE_TO_MANY)
1646         private final Integer[] f22 = arrayOfInteger;
1647         private final Integer[] g22 = f22;
1648 
1649         @SecondaryKey(relate=ONE_TO_MANY)
1650         private final Set<Integer> f23 = toSet(arrayOfInteger);
1651         private final Set<Integer> g23 = f23;
1652 
1653         @SecondaryKey(relate=ONE_TO_MANY)
1654         private final CompositeKey[] f24 = arrayOfCompositeKey;
1655         private final CompositeKey[] g24 = f24;
1656 
1657         @SecondaryKey(relate=ONE_TO_MANY)
1658         private final Set<CompositeKey> f25 = toSet(arrayOfCompositeKey);
1659         private final Set<CompositeKey> g25 = f25;
1660 
1661         @SecondaryKey(relate=MANY_TO_ONE)
1662         private final MyEnum f26 = MyEnum.TWO;
1663         private final MyEnum g26 = f26;
1664 
1665         @SecondaryKey(relate=ONE_TO_MANY)
1666         private final MyEnum[] f27 = arrayOfEnum;
1667         private final MyEnum[] g27 = f27;
1668 
1669         @SecondaryKey(relate=ONE_TO_MANY)
1670         private final Set<MyEnum> f28 = toSet(arrayOfEnum);
1671         private final Set<MyEnum> g28 = f28;
1672 
1673         @SecondaryKey(relate=MANY_TO_ONE)
1674         private final MyEnumCSM f41 = MyEnumCSM.B;
1675         private final MyEnumCSM g41 = f41;
1676 
1677         @SecondaryKey(relate=ONE_TO_MANY)
1678         private final MyEnumCSM[] f42 = arrayOfEnumCSM;
1679         private final MyEnumCSM[] g42 = f42;
1680 
1681         @SecondaryKey(relate=ONE_TO_MANY)
1682         private final Set<MyEnumCSM> f43 = toSet(arrayOfEnumCSM);
1683         private final Set<MyEnumCSM> g43 = f43;
1684 
1685         /* Repeated key values to test shared references. */
1686 
1687         @SecondaryKey(relate=MANY_TO_ONE)
1688         private final Date f31 = f11;
1689 
1690         @SecondaryKey(relate=MANY_TO_ONE)
1691         private final Boolean f32 = f12;
1692 
1693         @SecondaryKey(relate=MANY_TO_ONE)
1694         private final Character f33 = f13;
1695 
1696         @SecondaryKey(relate=MANY_TO_ONE)
1697         private final Byte f34 = f14;
1698 
1699         @SecondaryKey(relate=MANY_TO_ONE)
1700         private final Short f35 = f15;
1701 
1702         @SecondaryKey(relate=MANY_TO_ONE)
1703         private final Integer f36 = f16;
1704 
1705         @SecondaryKey(relate=MANY_TO_ONE)
1706         private final Long f37= f17;
1707 
1708         @SecondaryKey(relate=MANY_TO_ONE)
1709         private final Float f38 = f18;
1710 
1711         @SecondaryKey(relate=MANY_TO_ONE)
1712         private final Double f39 = f19;
1713 
1714         @SecondaryKey(relate=MANY_TO_ONE)
1715         private final CompositeKey f40 = f20;
1716 
getPriKeyObject()1717         public Object getPriKeyObject() {
1718             return id;
1719         }
1720 
validate(Object other)1721         public void validate(Object other) {
1722             SecKeys o = (SecKeys) other;
1723             TestCase.assertEquals(id, o.id);
1724 
1725             TestCase.assertEquals(f0, o.f0);
1726             TestCase.assertEquals(f1, o.f1);
1727             TestCase.assertEquals(f2, o.f2);
1728             TestCase.assertEquals(f3, o.f3);
1729             TestCase.assertEquals(f4, o.f4);
1730             TestCase.assertEquals(f5, o.f5);
1731             TestCase.assertEquals(f6, o.f6);
1732             TestCase.assertEquals(f7, o.f7);
1733             TestCase.assertEquals(f8, o.f8);
1734             TestCase.assertEquals(f9, o.f9);
1735             TestCase.assertEquals(f10, o.f10);
1736             TestCase.assertEquals(f11, o.f11);
1737             TestCase.assertEquals(f12, o.f12);
1738             TestCase.assertEquals(f13, o.f13);
1739             TestCase.assertEquals(f14, o.f14);
1740             TestCase.assertEquals(f15, o.f15);
1741             TestCase.assertEquals(f16, o.f16);
1742             TestCase.assertEquals(f17, o.f17);
1743             TestCase.assertEquals(f18, o.f18);
1744             TestCase.assertEquals(f19, o.f19);
1745             TestCase.assertEquals(f20, o.f20);
1746             TestCase.assertTrue(Arrays.equals(f21, o.f21));
1747             TestCase.assertTrue(Arrays.equals(f22, o.f22));
1748             TestCase.assertEquals(f23, o.f23);
1749             TestCase.assertTrue(Arrays.equals(f24, o.f24));
1750             TestCase.assertEquals(f25, o.f25);
1751             TestCase.assertEquals(f26, o.f26);
1752             TestCase.assertTrue(Arrays.equals(f27, o.f27));
1753             TestCase.assertEquals(f28, o.f28);
1754             TestCase.assertEquals(f41, o.f41);
1755             TestCase.assertTrue(Arrays.equals(f42, o.f42));
1756             TestCase.assertEquals(f43, o.f43);
1757 
1758             TestCase.assertEquals(g0, o.g0);
1759             TestCase.assertEquals(g1, o.g1);
1760             TestCase.assertEquals(g2, o.g2);
1761             TestCase.assertEquals(g3, o.g3);
1762             TestCase.assertEquals(g4, o.g4);
1763             TestCase.assertEquals(g5, o.g5);
1764             TestCase.assertEquals(g6, o.g6);
1765             TestCase.assertEquals(g7, o.g7);
1766             TestCase.assertEquals(g8, o.g8);
1767             TestCase.assertEquals(g9, o.g9);
1768             TestCase.assertEquals(g10, o.g10);
1769             TestCase.assertEquals(g11, o.g11);
1770             TestCase.assertEquals(g12, o.g12);
1771             TestCase.assertEquals(g13, o.g13);
1772             TestCase.assertEquals(g14, o.g14);
1773             TestCase.assertEquals(g15, o.g15);
1774             TestCase.assertEquals(g16, o.g16);
1775             TestCase.assertEquals(g17, o.g17);
1776             TestCase.assertEquals(g18, o.g18);
1777             TestCase.assertEquals(g19, o.g19);
1778             TestCase.assertEquals(g20, o.g20);
1779             TestCase.assertTrue(Arrays.equals(g21, o.g21));
1780             TestCase.assertTrue(Arrays.equals(g22, o.g22));
1781             TestCase.assertEquals(g23, o.g23);
1782             TestCase.assertTrue(Arrays.equals(g24, o.g24));
1783             TestCase.assertEquals(g25, o.g25);
1784             TestCase.assertEquals(g26, o.g26);
1785             TestCase.assertTrue(Arrays.equals(g27, o.g27));
1786             TestCase.assertEquals(g28, o.g28);
1787             TestCase.assertEquals(g41, o.g41);
1788             TestCase.assertTrue(Arrays.equals(g42, o.g42));
1789             TestCase.assertEquals(g43, o.g43);
1790 
1791             TestCase.assertEquals(f31, o.f31);
1792             TestCase.assertEquals(f32, o.f32);
1793             TestCase.assertEquals(f33, o.f33);
1794             TestCase.assertEquals(f34, o.f34);
1795             TestCase.assertEquals(f35, o.f35);
1796             TestCase.assertEquals(f36, o.f36);
1797             TestCase.assertEquals(f37, o.f37);
1798             TestCase.assertEquals(f38, o.f38);
1799             TestCase.assertEquals(f39, o.f39);
1800             TestCase.assertEquals(f40, o.f40);
1801 
1802             checkSameIfNonNull(o.f31, o.f11);
1803             checkSameIfNonNull(o.f32, o.f12);
1804             checkSameIfNonNull(o.f33, o.f13);
1805             checkSameIfNonNull(o.f34, o.f14);
1806             checkSameIfNonNull(o.f35, o.f15);
1807             checkSameIfNonNull(o.f36, o.f16);
1808             checkSameIfNonNull(o.f37, o.f17);
1809             checkSameIfNonNull(o.f38, o.f18);
1810             checkSameIfNonNull(o.f39, o.f19);
1811             checkSameIfNonNull(o.f40, o.f20);
1812         }
1813     }
1814 
1815     @Test
testSecKeyRefToPriKey()1816     public void testSecKeyRefToPriKey()
1817         throws FileNotFoundException, DatabaseException {
1818 
1819         open();
1820 
1821         SecKeyRefToPriKey obj = new SecKeyRefToPriKey();
1822         checkEntity(SecKeyRefToPriKey.class, obj);
1823 
1824         checkMetadata(SecKeyRefToPriKey.class.getName(), new String[][] {
1825                           {"priKey", "java.lang.String"},
1826                           {"secKey1", "java.lang.String"},
1827                           {"secKey2", String[].class.getName()},
1828                           {"secKey3", Set.class.getName()},
1829                       },
1830                       0 /*priKeyIndex*/, null);
1831 
1832         checkSecKey(obj, "secKey1", obj.secKey1, String.class);
1833         checkSecMultiKey(obj, "secKey2", toSet(obj.secKey2), String.class);
1834         checkSecMultiKey(obj, "secKey3", toSet(obj.secKey3), String.class);
1835 
1836         close();
1837     }
1838 
1839     @Entity
1840     static class SecKeyRefToPriKey implements MyEntity {
1841 
1842         @PrimaryKey
1843         private final String priKey;
1844 
1845         @SecondaryKey(relate=ONE_TO_ONE)
1846         private final String secKey1;
1847 
1848         @SecondaryKey(relate=ONE_TO_MANY)
1849         private final String[] secKey2;
1850 
1851         @SecondaryKey(relate=ONE_TO_MANY)
1852         private final Set<String> secKey3 = new HashSet<String>();
1853 
SecKeyRefToPriKey()1854         private SecKeyRefToPriKey() {
1855             priKey = "sharedValue";
1856             secKey1 = priKey;
1857             secKey2 = new String[] { priKey };
1858             secKey3.add(priKey);
1859         }
1860 
getPriKeyObject()1861         public Object getPriKeyObject() {
1862             return priKey;
1863         }
1864 
validate(Object other)1865         public void validate(Object other) {
1866             SecKeyRefToPriKey o = (SecKeyRefToPriKey) other;
1867             TestCase.assertEquals(priKey, o.priKey);
1868             TestCase.assertNotNull(o.secKey1);
1869             TestCase.assertEquals(1, o.secKey2.length);
1870             TestCase.assertEquals(1, o.secKey3.size());
1871         }
1872     }
1873 
1874     @Test
testSecKeyInSuperclass()1875     public void testSecKeyInSuperclass()
1876         throws FileNotFoundException, DatabaseException {
1877 
1878         open();
1879 
1880         SecKeyInSuperclassEntity obj = new SecKeyInSuperclassEntity();
1881         checkEntity(SecKeyInSuperclassEntity.class, obj);
1882 
1883         checkMetadata(SecKeyInSuperclass.class.getName(),
1884                       new String[][] {
1885                           {"priKey", "java.lang.String"},
1886                           {"secKey1", String.class.getName()},
1887                       },
1888                       0/*priKeyIndex*/, null);
1889 
1890         checkMetadata(SecKeyInSuperclassEntity.class.getName(),
1891                       new String[][] {
1892                           {"secKey2", "java.lang.String"},
1893                       },
1894                       -1 /*priKeyIndex*/, SecKeyInSuperclass.class.getName());
1895 
1896         checkSecKey
1897             (obj, SecKeyInSuperclassEntity.class, "secKey1", obj.secKey1,
1898              String.class);
1899         checkSecKey
1900             (obj, SecKeyInSuperclassEntity.class, "secKey2", obj.secKey2,
1901              String.class);
1902 
1903         close();
1904     }
1905 
1906     @Persistent
1907     static class SecKeyInSuperclass implements MyEntity {
1908 
1909         @PrimaryKey
1910         String priKey = "1";
1911 
1912         @SecondaryKey(relate=ONE_TO_ONE)
1913         String secKey1 = "1";
1914 
getPriKeyObject()1915         public Object getPriKeyObject() {
1916             return priKey;
1917         }
1918 
validate(Object other)1919         public void validate(Object other) {
1920             SecKeyInSuperclass o = (SecKeyInSuperclass) other;
1921             TestCase.assertEquals(secKey1, o.secKey1);
1922         }
1923     }
1924 
1925     @Entity
1926     static class SecKeyInSuperclassEntity extends SecKeyInSuperclass {
1927 
1928         @SecondaryKey(relate=ONE_TO_ONE)
1929         String secKey2 = "2";
1930 
1931         @Override
validate(Object other)1932         public void validate(Object other) {
1933             super.validate(other);
1934             SecKeyInSuperclassEntity o = (SecKeyInSuperclassEntity) other;
1935             TestCase.assertEquals(priKey, o.priKey);
1936             TestCase.assertEquals(secKey2, o.secKey2);
1937         }
1938     }
1939 
1940     @Test
testSecKeyInSubclass()1941     public void testSecKeyInSubclass()
1942         throws FileNotFoundException, DatabaseException {
1943 
1944         open();
1945 
1946         SecKeyInSubclass obj = new SecKeyInSubclass();
1947         checkEntity(SecKeyInSubclassEntity.class, obj);
1948 
1949         checkMetadata(SecKeyInSubclassEntity.class.getName(), new String[][] {
1950                           {"priKey", "java.lang.String"},
1951                           {"secKey1", "java.lang.String"},
1952                       },
1953                       0 /*priKeyIndex*/, null);
1954 
1955         checkMetadata(SecKeyInSubclass.class.getName(), new String[][] {
1956                           {"secKey2", String.class.getName()},
1957                       },
1958                       -1 /*priKeyIndex*/,
1959                       SecKeyInSubclassEntity.class.getName());
1960 
1961         checkSecKey
1962             (obj, SecKeyInSubclassEntity.class, "secKey1", obj.secKey1,
1963              String.class);
1964         checkSecKey
1965             (obj, SecKeyInSubclassEntity.class, "secKey2", obj.secKey2,
1966              String.class);
1967 
1968         close();
1969     }
1970 
1971     @Entity
1972     static class SecKeyInSubclassEntity implements MyEntity {
1973 
1974         @PrimaryKey
1975         String priKey = "1";
1976 
1977         @SecondaryKey(relate=ONE_TO_ONE)
1978         String secKey1;
1979 
getPriKeyObject()1980         public Object getPriKeyObject() {
1981             return priKey;
1982         }
1983 
validate(Object other)1984         public void validate(Object other) {
1985             SecKeyInSubclassEntity o = (SecKeyInSubclassEntity) other;
1986             TestCase.assertEquals(priKey, o.priKey);
1987             TestCase.assertEquals(secKey1, o.secKey1);
1988         }
1989     }
1990 
1991     @Persistent
1992     static class SecKeyInSubclass extends SecKeyInSubclassEntity {
1993 
1994         @SecondaryKey(relate=ONE_TO_ONE)
1995         String secKey2 = "2";
1996 
1997         @Override
validate(Object other)1998         public void validate(Object other) {
1999             super.validate(other);
2000             SecKeyInSubclass o = (SecKeyInSubclass) other;
2001             TestCase.assertEquals(secKey2, o.secKey2);
2002         }
2003     }
2004 
checkSameIfNonNull(Object o1, Object o2)2005     private static void checkSameIfNonNull(Object o1, Object o2) {
2006         if (o1 != null && o2 != null) {
2007             assertSame(o1, o2);
2008         }
2009     }
2010 
checkEntity(Class entityCls, MyEntity entity)2011     private void checkEntity(Class entityCls, MyEntity entity) {
2012         Object priKey = entity.getPriKeyObject();
2013         Class keyCls = priKey.getClass();
2014         DatabaseEntry keyEntry2 = new DatabaseEntry();
2015         DatabaseEntry dataEntry2 = new DatabaseEntry();
2016 
2017         /* Write object, read it back and validate (compare) it. */
2018         PersistEntityBinding entityBinding =
2019             new PersistEntityBinding(catalog, entityCls.getName(), false);
2020         entityBinding.objectToData(entity, dataEntry);
2021         entityBinding.objectToKey(entity, keyEntry);
2022         Object entity2 = entityBinding.entryToObject(keyEntry, dataEntry);
2023         entity.validate(entity2);
2024 
2025         /* Read back the primary key and validate it. */
2026         PersistKeyBinding keyBinding =
2027             new PersistKeyBinding(catalog, keyCls.getName(), false);
2028         Object priKey2 = keyBinding.entryToObject(keyEntry);
2029         assertEquals(priKey, priKey2);
2030         keyBinding.objectToEntry(priKey2, keyEntry2);
2031         assertEquals(keyEntry, keyEntry2);
2032 
2033         /* Check raw entity binding. */
2034         PersistEntityBinding rawEntityBinding =
2035             new PersistEntityBinding(catalog, entityCls.getName(), true);
2036         RawObject rawEntity =
2037             (RawObject) rawEntityBinding.entryToObject(keyEntry, dataEntry);
2038         rawEntityBinding.objectToKey(rawEntity, keyEntry2);
2039         rawEntityBinding.objectToData(rawEntity, dataEntry2);
2040         entity2 = entityBinding.entryToObject(keyEntry2, dataEntry2);
2041         entity.validate(entity2);
2042         RawObject rawEntity2 =
2043             (RawObject) rawEntityBinding.entryToObject(keyEntry2, dataEntry2);
2044         assertEquals(rawEntity, rawEntity2);
2045         assertEquals(dataEntry, dataEntry2);
2046         assertEquals(keyEntry, keyEntry2);
2047 
2048         /* Check that raw entity can be converted to a regular entity. */
2049         try {
2050             entity2 = catalog.convertRawObject(rawEntity, null);
2051         } catch (RefreshException e) {
2052             fail(e.toString());
2053         }
2054         entity.validate(entity2);
2055 
2056         /* Check raw key binding. */
2057         PersistKeyBinding rawKeyBinding =
2058             new PersistKeyBinding(catalog, keyCls.getName(), true);
2059         Object rawKey = rawKeyBinding.entryToObject(keyEntry);
2060         rawKeyBinding.objectToEntry(rawKey, keyEntry2);
2061         priKey2 = keyBinding.entryToObject(keyEntry2);
2062         assertEquals(priKey, priKey2);
2063         assertEquals(keyEntry, keyEntry2);
2064     }
2065 
checkSecKey(MyEntity entity, String keyName, Object keyValue, Class keyCls)2066     private void checkSecKey(MyEntity entity,
2067                              String keyName,
2068                              Object keyValue,
2069                              Class keyCls)
2070         throws DatabaseException {
2071 
2072         checkSecKey(entity, entity.getClass(), keyName, keyValue, keyCls);
2073     }
2074 
checkSecKey(MyEntity entity, Class entityCls, String keyName, Object keyValue, Class keyCls)2075     private void checkSecKey(MyEntity entity,
2076                              Class entityCls,
2077                              String keyName,
2078                              Object keyValue,
2079                              Class keyCls)
2080         throws DatabaseException {
2081 
2082         /* Get entity metadata. */
2083         EntityMetadata entityMeta =
2084             model.getEntityMetadata(entityCls.getName());
2085         assertNotNull(entityMeta);
2086 
2087         /* Get secondary key metadata. */
2088         SecondaryKeyMetadata secKeyMeta =
2089             entityMeta.getSecondaryKeys().get(keyName);
2090         assertNotNull(secKeyMeta);
2091 
2092         /* Create key creator/nullifier. */
2093         SecondaryKeyCreator keyCreator = new PersistKeyCreator
2094             (catalog, entityMeta, keyCls.getName(), secKeyMeta,
2095              false /*rawAcess*/);
2096 
2097         /* Convert entity to bytes. */
2098         PersistEntityBinding entityBinding =
2099             new PersistEntityBinding(catalog, entityCls.getName(), false);
2100         entityBinding.objectToData(entity, dataEntry);
2101         entityBinding.objectToKey(entity, keyEntry);
2102 
2103         /* Extract secondary key bytes from entity bytes. */
2104         DatabaseEntry secKeyEntry = new DatabaseEntry();
2105         boolean isKeyPresent = keyCreator.createSecondaryKey
2106             (null, keyEntry, dataEntry, secKeyEntry);
2107         assertEquals(keyValue != null, isKeyPresent);
2108 
2109         /* Convert secondary key bytes back to an object. */
2110         PersistKeyBinding keyBinding =
2111             new PersistKeyBinding(catalog, keyCls.getName(), false);
2112         if (isKeyPresent) {
2113             Object keyValue2 = keyBinding.entryToObject(secKeyEntry);
2114             assertEquals(keyValue, keyValue2);
2115             DatabaseEntry secKeyEntry2 = new DatabaseEntry();
2116             keyBinding.objectToEntry(keyValue2, secKeyEntry2);
2117             assertEquals(secKeyEntry, secKeyEntry2);
2118         }
2119     }
2120 
checkSecMultiKey(MyEntity entity, String keyName, Set keyValues, Class keyCls)2121     private void checkSecMultiKey(MyEntity entity,
2122                                   String keyName,
2123                                   Set keyValues,
2124                                   Class keyCls)
2125         throws DatabaseException {
2126 
2127         /* Get entity metadata. */
2128         Class entityCls = entity.getClass();
2129         EntityMetadata entityMeta =
2130             model.getEntityMetadata(entityCls.getName());
2131         assertNotNull(entityMeta);
2132 
2133         /* Get secondary key metadata. */
2134         SecondaryKeyMetadata secKeyMeta =
2135             entityMeta.getSecondaryKeys().get(keyName);
2136         assertNotNull(secKeyMeta);
2137 
2138         /* Create key creator/nullifier. */
2139         SecondaryMultiKeyCreator keyCreator = new PersistKeyCreator
2140             (catalog, entityMeta, keyCls.getName(), secKeyMeta,
2141              false /*rawAcess*/);
2142 
2143         /* Convert entity to bytes. */
2144         PersistEntityBinding entityBinding =
2145             new PersistEntityBinding(catalog, entityCls.getName(), false);
2146         entityBinding.objectToData(entity, dataEntry);
2147         entityBinding.objectToKey(entity, keyEntry);
2148 
2149         /* Extract secondary key bytes from entity bytes. */
2150         Set<DatabaseEntry> results = new HashSet<DatabaseEntry>();
2151         keyCreator.createSecondaryKeys
2152             (null, keyEntry, dataEntry, results);
2153         assertEquals(keyValues.size(), results.size());
2154 
2155         /* Convert secondary key bytes back to objects. */
2156         PersistKeyBinding keyBinding =
2157             new PersistKeyBinding(catalog, keyCls.getName(), false);
2158         Set keyValues2 = new HashSet();
2159         for (DatabaseEntry secKeyEntry : results) {
2160             Object keyValue2 = keyBinding.entryToObject(secKeyEntry);
2161             keyValues2.add(keyValue2);
2162         }
2163         assertEquals(keyValues, keyValues2);
2164     }
2165 
nullifySecKey(MyEntity entity, String keyName, Object keyValue, Class keyCls)2166     private void nullifySecKey(MyEntity entity,
2167                               String keyName,
2168                               Object keyValue,
2169                               Class keyCls)
2170         throws DatabaseException {
2171 
2172         /* Get entity metadata. */
2173         Class entityCls = entity.getClass();
2174         EntityMetadata entityMeta =
2175             model.getEntityMetadata(entityCls.getName());
2176         assertNotNull(entityMeta);
2177 
2178         /* Get secondary key metadata. */
2179         SecondaryKeyMetadata secKeyMeta =
2180             entityMeta.getSecondaryKeys().get(keyName);
2181         assertNotNull(secKeyMeta);
2182 
2183         /* Create key creator/nullifier. */
2184         ForeignMultiKeyNullifier keyNullifier = new PersistKeyCreator
2185             (catalog, entityMeta, keyCls.getName(), secKeyMeta,
2186              false /*rawAcess*/);
2187 
2188         /* Convert entity to bytes. */
2189         PersistEntityBinding entityBinding =
2190             new PersistEntityBinding(catalog, entityCls.getName(), false);
2191         entityBinding.objectToData(entity, dataEntry);
2192         entityBinding.objectToKey(entity, keyEntry);
2193 
2194         /* Convert secondary key to bytes. */
2195         PersistKeyBinding keyBinding =
2196             new PersistKeyBinding(catalog, keyCls.getName(), false);
2197         DatabaseEntry secKeyEntry = new DatabaseEntry();
2198         if (keyValue != null) {
2199             keyBinding.objectToEntry(keyValue, secKeyEntry);
2200         }
2201 
2202         /* Nullify secondary key bytes within entity bytes. */
2203         boolean isKeyPresent = keyNullifier.nullifyForeignKey
2204             (null, keyEntry, dataEntry, secKeyEntry);
2205         assertEquals(keyValue != null, isKeyPresent);
2206 
2207         /* Convert modified entity bytes back to an entity. */
2208         Object entity2 = entityBinding.entryToObject(keyEntry, dataEntry);
2209         setFieldToNull(entity, keyName);
2210         entity.validate(entity2);
2211 
2212         /* Do a full check after nullifying it. */
2213         checkSecKey(entity, keyName, null, keyCls);
2214     }
2215 
nullifySecMultiKey(MyEntity entity, String keyName, Object keyValue, Class keyCls)2216     private void nullifySecMultiKey(MyEntity entity,
2217                                     String keyName,
2218                                     Object keyValue,
2219                                     Class keyCls)
2220         throws DatabaseException {
2221 
2222         /* Get entity metadata. */
2223         Class entityCls = entity.getClass();
2224         EntityMetadata entityMeta =
2225             model.getEntityMetadata(entityCls.getName());
2226         assertNotNull(entityMeta);
2227 
2228         /* Get secondary key metadata. */
2229         SecondaryKeyMetadata secKeyMeta =
2230             entityMeta.getSecondaryKeys().get(keyName);
2231         assertNotNull(secKeyMeta);
2232 
2233         /* Create key creator/nullifier. */
2234         ForeignMultiKeyNullifier keyNullifier = new PersistKeyCreator
2235             (catalog, entityMeta, keyCls.getName(), secKeyMeta,
2236              false /*rawAcess*/);
2237 
2238         /* Convert entity to bytes. */
2239         PersistEntityBinding entityBinding =
2240             new PersistEntityBinding(catalog, entityCls.getName(), false);
2241         entityBinding.objectToData(entity, dataEntry);
2242         entityBinding.objectToKey(entity, keyEntry);
2243 
2244         /* Get secondary key binding. */
2245         PersistKeyBinding keyBinding =
2246             new PersistKeyBinding(catalog, keyCls.getName(), false);
2247         DatabaseEntry secKeyEntry = new DatabaseEntry();
2248 
2249         /* Nullify one key value at a time until all of them are gone. */
2250         while (true) {
2251             Object fieldObj = getField(entity, keyName);
2252             fieldObj = nullifyFirstElement(fieldObj, keyBinding, secKeyEntry);
2253             if (fieldObj == null) {
2254                 break;
2255             }
2256             setField(entity, keyName, fieldObj);
2257 
2258             /* Nullify secondary key bytes within entity bytes. */
2259             boolean isKeyPresent = keyNullifier.nullifyForeignKey
2260                 (null, keyEntry, dataEntry, secKeyEntry);
2261             assertEquals(keyValue != null, isKeyPresent);
2262 
2263             /* Convert modified entity bytes back to an entity. */
2264             Object entity2 = entityBinding.entryToObject(keyEntry, dataEntry);
2265             entity.validate(entity2);
2266 
2267             /* Do a full check after nullifying it. */
2268             Set keyValues;
2269             if (fieldObj instanceof Set) {
2270                 keyValues = (Set) fieldObj;
2271             } else if (fieldObj instanceof Object[]) {
2272                 keyValues = toSet((Object[]) fieldObj);
2273             } else if (fieldObj instanceof int[]) {
2274                 keyValues = toSet((int[]) fieldObj);
2275             } else {
2276                 throw new IllegalStateException(fieldObj.getClass().getName());
2277             }
2278             checkSecMultiKey(entity, keyName, keyValues, keyCls);
2279         }
2280     }
2281 
2282     /**
2283      * Nullifies the first element of an array or collection object by removing
2284      * it from the array or collection.  Returns the resulting array or
2285      * collection.  Also outputs the removed element to the keyEntry using the
2286      * keyBinding.
2287      */
nullifyFirstElement(Object obj, EntryBinding keyBinding, DatabaseEntry keyEntry)2288     private Object nullifyFirstElement(Object obj,
2289                                        EntryBinding keyBinding,
2290                                        DatabaseEntry keyEntry) {
2291         if (obj instanceof Collection) {
2292             Iterator i = ((Collection) obj).iterator();
2293             if (i.hasNext()) {
2294                 Object elem = i.next();
2295                 i.remove();
2296                 keyBinding.objectToEntry(elem, keyEntry);
2297                 return obj;
2298             } else {
2299                 return null;
2300             }
2301         } else if (obj instanceof Object[]) {
2302             Object[] a1 = (Object[]) obj;
2303             if (a1.length > 0) {
2304                 Object[] a2 = (Object[]) Array.newInstance
2305                     (obj.getClass().getComponentType(), a1.length - 1);
2306                 System.arraycopy(a1, 1, a2, 0, a2.length);
2307                 keyBinding.objectToEntry(a1[0], keyEntry);
2308                 return a2;
2309             } else {
2310                 return null;
2311             }
2312         } else if (obj instanceof int[]) {
2313             int[] a1 = (int[]) obj;
2314             if (a1.length > 0) {
2315                 int[] a2 = new int[a1.length - 1];
2316                 System.arraycopy(a1, 1, a2, 0, a2.length);
2317                 keyBinding.objectToEntry(a1[0], keyEntry);
2318                 return a2;
2319             } else {
2320                 return null;
2321             }
2322         } else {
2323             throw new IllegalStateException(obj.getClass().getName());
2324         }
2325     }
2326 
checkMetadata(String clsName, String[][] nameTypePairs, int priKeyIndex, String superClsName)2327     private void checkMetadata(String clsName,
2328                                String[][] nameTypePairs,
2329                                int priKeyIndex,
2330                                String superClsName)
2331         throws DatabaseException {
2332 
2333         /* Check metadata/types against the live model. */
2334         checkMetadata
2335             (catalog, model, clsName, nameTypePairs, priKeyIndex,
2336              superClsName);
2337 
2338         /*
2339          * Open a catalog that uses the stored model.
2340          */
2341         PersistCatalog storedCatalog = null;
2342         storedCatalog = new PersistCatalog
2343             (env, STORE_PREFIX, STORE_PREFIX + "catalog", new DatabaseConfig(),
2344              null, null, false /*useCurrentModel*/, null /*Store*/);
2345         EntityModel storedModel = storedCatalog.getResolvedModel();
2346 
2347         /* Check metadata/types against the stored catalog/model. */
2348         checkMetadata
2349             (storedCatalog, storedModel, clsName, nameTypePairs, priKeyIndex,
2350              superClsName);
2351 
2352         storedCatalog.close();
2353     }
2354 
checkMetadata(PersistCatalog checkCatalog, EntityModel checkModel, String clsName, String[][] nameTypePairs, int priKeyIndex, String superClsName)2355     private void checkMetadata(PersistCatalog checkCatalog,
2356                                EntityModel checkModel,
2357                                String clsName,
2358                                String[][] nameTypePairs,
2359                                int priKeyIndex,
2360                                String superClsName) {
2361         ClassMetadata classMeta = checkModel.getClassMetadata(clsName);
2362         assertNotNull(clsName, classMeta);
2363 
2364         PrimaryKeyMetadata priKeyMeta = classMeta.getPrimaryKey();
2365         if (priKeyIndex >= 0) {
2366             assertNotNull(priKeyMeta);
2367             String fieldName = nameTypePairs[priKeyIndex][0];
2368             String fieldType = nameTypePairs[priKeyIndex][1];
2369             assertEquals(priKeyMeta.getName(), fieldName);
2370             assertEquals(priKeyMeta.getClassName(), fieldType);
2371             assertEquals(priKeyMeta.getDeclaringClassName(), clsName);
2372             assertNull(priKeyMeta.getSequenceName());
2373         } else {
2374             assertNull(priKeyMeta);
2375         }
2376 
2377         RawType type = checkCatalog.getFormat(clsName);
2378         assertNotNull(type);
2379         assertEquals(clsName, type.getClassName());
2380         assertEquals(0, type.getVersion());
2381         assertTrue(!type.isSimple());
2382         assertTrue(!type.isPrimitive());
2383         assertTrue(!type.isEnum());
2384         assertNull(type.getEnumConstants());
2385         assertTrue(!type.isArray());
2386         assertEquals(0, type.getDimensions());
2387         assertNull(type.getComponentType());
2388         RawType superType = type.getSuperType();
2389         if (superClsName != null) {
2390             assertNotNull(superType);
2391             assertEquals(superClsName, superType.getClassName());
2392         } else {
2393             assertNull(superType);
2394         }
2395 
2396         Map<String, RawField> fields = type.getFields();
2397         assertNotNull(fields);
2398 
2399         int nFields = nameTypePairs.length;
2400         assertEquals(nFields, fields.size());
2401 
2402         for (String[] pair : nameTypePairs) {
2403             String fieldName = pair[0];
2404             String fieldType = pair[1];
2405             Class fieldCls;
2406             try {
2407                 fieldCls = checkCatalog.resolveClass(fieldType);
2408             } catch (ClassNotFoundException e) {
2409                 fail(e.toString());
2410                 return; /* For compiler */
2411             }
2412             RawField field = fields.get(fieldName);
2413             assertNotNull(field);
2414             assertEquals(fieldName, field.getName());
2415             type = field.getType();
2416             assertNotNull(type);
2417             int dim = getArrayDimensions(fieldType);
2418             while (dim > 0) {
2419                 assertEquals(dim, type.getDimensions());
2420                 assertEquals(dim, getArrayDimensions(fieldType));
2421                 assertEquals(true, type.isArray());
2422                 assertEquals(fieldType, type.getClassName());
2423                 assertEquals(0, type.getVersion());
2424                 assertTrue(!type.isSimple());
2425                 assertTrue(!type.isPrimitive());
2426                 assertTrue(!type.isEnum());
2427                 assertNull(type.getEnumConstants());
2428                 fieldType = getArrayComponent(fieldType, dim);
2429                 type = type.getComponentType();
2430                 assertNotNull(fieldType, type);
2431                 dim -= 1;
2432             }
2433             assertEquals(fieldType, type.getClassName());
2434             List<String> enums = getEnumConstants(fieldType);
2435             assertEquals(isSimpleType(fieldType), type.isSimple());
2436             assertEquals(isPrimitiveType(fieldType), type.isPrimitive());
2437             assertNull(type.getComponentType());
2438             assertTrue(!type.isArray());
2439             assertEquals(0, type.getDimensions());
2440             if (enums != null) {
2441                 assertTrue(type.isEnum());
2442                 assertEquals(enums, type.getEnumConstants());
2443                 assertNull(type.getSuperType());
2444             } else {
2445                 assertTrue(!type.isEnum());
2446                 assertNull(type.getEnumConstants());
2447             }
2448         }
2449     }
2450 
getEnumConstants(String clsName)2451     private List<String> getEnumConstants(String clsName) {
2452         if (isPrimitiveType(clsName)) {
2453             return null;
2454         }
2455         Class cls;
2456         try {
2457             cls = Class.forName(clsName);
2458         } catch (ClassNotFoundException e) {
2459             fail(e.toString());
2460             return null; /* Never happens. */
2461         }
2462         if (!cls.isEnum()) {
2463             return null;
2464         }
2465         List<String> enums = new ArrayList<String>();
2466         Object[] vals = cls.getEnumConstants();
2467         for (Object val : vals) {
2468             enums.add(val.toString());
2469         }
2470         return enums;
2471     }
2472 
getArrayComponent(String clsName, int dim)2473     private String getArrayComponent(String clsName, int dim) {
2474         clsName = clsName.substring(1);
2475         if (dim > 1) {
2476             return clsName;
2477         }
2478         if (clsName.charAt(0) == 'L' &&
2479             clsName.charAt(clsName.length() - 1) == ';') {
2480             return clsName.substring(1, clsName.length() - 1);
2481         }
2482         if (clsName.length() != 1) {
2483             fail();
2484         }
2485         switch (clsName.charAt(0)) {
2486         case 'Z': return "boolean";
2487         case 'B': return "byte";
2488         case 'C': return "char";
2489         case 'D': return "double";
2490         case 'F': return "float";
2491         case 'I': return "int";
2492         case 'J': return "long";
2493         case 'S': return "short";
2494         default: fail();
2495         }
2496         return null; /* Should never happen. */
2497     }
2498 
getArrayDimensions(String clsName)2499     private static int getArrayDimensions(String clsName) {
2500         int i = 0;
2501         while (clsName.charAt(i) == '[') {
2502             i += 1;
2503         }
2504         return i;
2505     }
2506 
isSimpleType(String clsName)2507     private static boolean isSimpleType(String clsName) {
2508         return isPrimitiveType(clsName) ||
2509                clsName.equals("java.lang.Boolean") ||
2510                clsName.equals("java.lang.Character") ||
2511                clsName.equals("java.lang.Byte") ||
2512                clsName.equals("java.lang.Short") ||
2513                clsName.equals("java.lang.Integer") ||
2514                clsName.equals("java.lang.Long") ||
2515                clsName.equals("java.lang.Float") ||
2516                clsName.equals("java.lang.Double") ||
2517                clsName.equals("java.lang.String") ||
2518                clsName.equals("java.math.BigInteger") ||
2519                clsName.equals("java.math.BigDecimal") ||
2520                clsName.equals("java.util.Date");
2521     }
2522 
isPrimitiveType(String clsName)2523     private static boolean isPrimitiveType(String clsName) {
2524         return clsName.equals("boolean") ||
2525                clsName.equals("char") ||
2526                clsName.equals("byte") ||
2527                clsName.equals("short") ||
2528                clsName.equals("int") ||
2529                clsName.equals("long") ||
2530                clsName.equals("float") ||
2531                clsName.equals("double");
2532     }
2533 
2534     interface MyEntity {
getPriKeyObject()2535         Object getPriKeyObject();
validate(Object other)2536         void validate(Object other);
2537     }
2538 
nullOrEqual(Object o1, Object o2)2539     private static boolean nullOrEqual(Object o1, Object o2) {
2540         return (o1 != null) ? o1.equals(o2) : (o2 == null);
2541     }
2542 
arrayToString(Object[] array)2543     private static String arrayToString(Object[] array) {
2544         StringBuilder buf = new StringBuilder();
2545         buf.append('[');
2546         for (Object o : array) {
2547             if (o instanceof Object[]) {
2548                 buf.append(arrayToString((Object[]) o));
2549             } else {
2550                 buf.append(o);
2551             }
2552             buf.append(',');
2553         }
2554         buf.append(']');
2555         return buf.toString();
2556     }
2557 
setFieldToNull(Object obj, String fieldName)2558     private void setFieldToNull(Object obj, String fieldName) {
2559         try {
2560             Field field = obj.getClass().getDeclaredField(fieldName);
2561             field.setAccessible(true);
2562             field.set(obj, null);
2563         } catch (NoSuchFieldException e) {
2564             fail(e.toString());
2565         } catch (IllegalAccessException e) {
2566             fail(e.toString());
2567         }
2568     }
2569 
setField(Object obj, String fieldName, Object fieldValue)2570     private void setField(Object obj, String fieldName, Object fieldValue) {
2571         try {
2572             Field field = obj.getClass().getDeclaredField(fieldName);
2573             field.setAccessible(true);
2574             field.set(obj, fieldValue);
2575         } catch (NoSuchFieldException e) {
2576             throw new IllegalStateException(e.toString());
2577         } catch (IllegalAccessException e) {
2578             throw new IllegalStateException(e.toString());
2579         }
2580     }
2581 
getField(Object obj, String fieldName)2582     private Object getField(Object obj, String fieldName) {
2583         try {
2584             Field field = obj.getClass().getDeclaredField(fieldName);
2585             field.setAccessible(true);
2586             return field.get(obj);
2587         } catch (NoSuchFieldException e) {
2588             throw new IllegalStateException(e.toString());
2589         } catch (IllegalAccessException e) {
2590             throw new IllegalStateException(e.toString());
2591         }
2592     }
2593 }
2594