001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: IndexTest.java,v 1.15.2.3 2008/01/07 15:14:35 cwl Exp $
007: */
008:
009: package com.sleepycat.persist.test;
010:
011: import static com.sleepycat.persist.model.Relationship.MANY_TO_MANY;
012: import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
013: import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
014: import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
015:
016: import java.util.ArrayList;
017: import java.util.Collection;
018: import java.util.Collections;
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.Set;
022: import java.util.SortedMap;
023: import java.util.SortedSet;
024: import java.util.TreeMap;
025: import java.util.TreeSet;
026:
027: import junit.framework.Test;
028:
029: import com.sleepycat.collections.MapEntryParameter;
030: import com.sleepycat.je.DatabaseException;
031: import com.sleepycat.je.Transaction;
032: import com.sleepycat.je.test.TxnTestCase;
033: import com.sleepycat.persist.EntityCursor;
034: import com.sleepycat.persist.EntityIndex;
035: import com.sleepycat.persist.EntityStore;
036: import com.sleepycat.persist.PrimaryIndex;
037: import com.sleepycat.persist.SecondaryIndex;
038: import com.sleepycat.persist.StoreConfig;
039: import com.sleepycat.persist.model.Entity;
040: import com.sleepycat.persist.model.PrimaryKey;
041: import com.sleepycat.persist.model.SecondaryKey;
042: import com.sleepycat.persist.raw.RawObject;
043: import com.sleepycat.persist.raw.RawStore;
044: import com.sleepycat.persist.raw.RawType;
045:
046: /**
047: * Tests EntityIndex and EntityCursor in all their permutations.
048: *
049: * @author Mark Hayes
050: */
051: public class IndexTest extends TxnTestCase {
052:
053: private static final int N_RECORDS = 5;
054: private static final int THREE_TO_ONE = 3;
055:
056: public static Test suite() {
057: return txnTestSuite(IndexTest.class, null, null);
058: //new String[] { TxnTestCase.TXN_NULL});
059: }
060:
061: private EntityStore store;
062: private PrimaryIndex<Integer, MyEntity> primary;
063: private SecondaryIndex<Integer, Integer, MyEntity> oneToOne;
064: private SecondaryIndex<Integer, Integer, MyEntity> manyToOne;
065: private SecondaryIndex<Integer, Integer, MyEntity> oneToMany;
066: private SecondaryIndex<Integer, Integer, MyEntity> manyToMany;
067: private RawStore rawStore;
068: private RawType entityType;
069: private PrimaryIndex<Object, RawObject> primaryRaw;
070: private SecondaryIndex<Object, Object, RawObject> oneToOneRaw;
071: private SecondaryIndex<Object, Object, RawObject> manyToOneRaw;
072: private SecondaryIndex<Object, Object, RawObject> oneToManyRaw;
073: private SecondaryIndex<Object, Object, RawObject> manyToManyRaw;
074:
075: /**
076: * Opens the store.
077: */
078: private void open() throws DatabaseException {
079:
080: StoreConfig config = new StoreConfig();
081: config.setAllowCreate(envConfig.getAllowCreate());
082: config.setTransactional(envConfig.getTransactional());
083:
084: store = new EntityStore(env, "test", config);
085:
086: primary = store.getPrimaryIndex(Integer.class, MyEntity.class);
087: oneToOne = store.getSecondaryIndex(primary, Integer.class,
088: "oneToOne");
089: manyToOne = store.getSecondaryIndex(primary, Integer.class,
090: "manyToOne");
091: oneToMany = store.getSecondaryIndex(primary, Integer.class,
092: "oneToMany");
093: manyToMany = store.getSecondaryIndex(primary, Integer.class,
094: "manyToMany");
095:
096: assertNotNull(primary);
097: assertNotNull(oneToOne);
098: assertNotNull(manyToOne);
099: assertNotNull(oneToMany);
100: assertNotNull(manyToMany);
101:
102: rawStore = new RawStore(env, "test", config);
103: String clsName = MyEntity.class.getName();
104: entityType = rawStore.getModel().getRawType(clsName);
105: assertNotNull(entityType);
106:
107: primaryRaw = rawStore.getPrimaryIndex(clsName);
108: oneToOneRaw = rawStore.getSecondaryIndex(clsName, "oneToOne");
109: manyToOneRaw = rawStore.getSecondaryIndex(clsName, "manyToOne");
110: oneToManyRaw = rawStore.getSecondaryIndex(clsName, "oneToMany");
111: manyToManyRaw = rawStore.getSecondaryIndex(clsName,
112: "manyToMany");
113:
114: assertNotNull(primaryRaw);
115: assertNotNull(oneToOneRaw);
116: assertNotNull(manyToOneRaw);
117: assertNotNull(oneToManyRaw);
118: assertNotNull(manyToManyRaw);
119: }
120:
121: /**
122: * Closes the store.
123: */
124: private void close() throws DatabaseException {
125:
126: store.close();
127: store = null;
128: rawStore.close();
129: rawStore = null;
130: }
131:
132: /**
133: * The store must be closed before closing the environment.
134: */
135: public void tearDown() throws Exception {
136:
137: try {
138: if (rawStore != null) {
139: rawStore.close();
140: }
141: } catch (Throwable e) {
142: System.out.println("During tearDown: " + e);
143: }
144: try {
145: if (store != null) {
146: store.close();
147: }
148: } catch (Throwable e) {
149: System.out.println("During tearDown: " + e);
150: }
151: store = null;
152: rawStore = null;
153: super .tearDown();
154: }
155:
156: /**
157: * Primary keys: {0, 1, 2, 3, 4}
158: */
159: public void testPrimary() throws DatabaseException {
160:
161: SortedMap<Integer, SortedSet<Integer>> expected = new TreeMap<Integer, SortedSet<Integer>>();
162:
163: for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
164: SortedSet<Integer> values = new TreeSet<Integer>();
165: values.add(priKey);
166: expected.put(priKey, values);
167: }
168:
169: open();
170: addEntities(primary);
171: checkIndex(primary, expected, keyGetter, entityGetter);
172: checkIndex(primaryRaw, expected, rawKeyGetter, rawEntityGetter);
173:
174: /* Close and reopen, then recheck indices. */
175: close();
176: open();
177: checkIndex(primary, expected, keyGetter, entityGetter);
178: checkIndex(primaryRaw, expected, rawKeyGetter, rawEntityGetter);
179:
180: /* Check primary delete, last key first for variety. */
181: for (int priKey = N_RECORDS - 1; priKey >= 0; priKey -= 1) {
182: boolean useRaw = ((priKey & 1) != 0);
183: Transaction txn = txnBegin();
184: if (useRaw) {
185: primaryRaw.delete(txn, priKey);
186: } else {
187: primary.delete(txn, priKey);
188: }
189: txnCommit(txn);
190: expected.remove(priKey);
191: checkIndex(primary, expected, keyGetter, entityGetter);
192: }
193: checkAllEmpty();
194:
195: /* Check PrimaryIndex put operations. */
196: MyEntity e;
197: Transaction txn = txnBegin();
198: /* put() */
199: e = primary.put(txn, new MyEntity(1));
200: assertNull(e);
201: e = primary.get(txn, 1, null);
202: assertEquals(1, e.key);
203: /* putNoReturn() */
204: primary.putNoReturn(txn, new MyEntity(2));
205: e = primary.get(txn, 2, null);
206: assertEquals(2, e.key);
207: /* putNoOverwrite */
208: assertTrue(!primary.putNoOverwrite(txn, new MyEntity(1)));
209: assertTrue(!primary.putNoOverwrite(txn, new MyEntity(2)));
210: assertTrue(primary.putNoOverwrite(txn, new MyEntity(3)));
211: e = primary.get(txn, 3, null);
212: assertEquals(3, e.key);
213: txnCommit(txn);
214: close();
215: }
216:
217: /**
218: * { 0:0, 1:-1, 2:-2, 3:-3, 4:-4 }
219: */
220: public void testOneToOne() throws DatabaseException {
221:
222: SortedMap<Integer, SortedSet<Integer>> expected = new TreeMap<Integer, SortedSet<Integer>>();
223:
224: for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
225: SortedSet<Integer> values = new TreeSet<Integer>();
226: values.add(priKey);
227: Integer secKey = (-priKey);
228: expected.put(secKey, values);
229: }
230:
231: open();
232: addEntities(primary);
233: checkSecondary(oneToOne, oneToOneRaw, expected);
234: checkDelete(oneToOne, oneToOneRaw, expected);
235: close();
236: }
237:
238: /**
239: * { 0:0, 1:1, 2:2, 3:0, 4:1 }
240: */
241: public void testManyToOne() throws DatabaseException {
242:
243: SortedMap<Integer, SortedSet<Integer>> expected = new TreeMap<Integer, SortedSet<Integer>>();
244:
245: for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
246: Integer secKey = priKey % THREE_TO_ONE;
247: SortedSet<Integer> values = expected.get(secKey);
248: if (values == null) {
249: values = new TreeSet<Integer>();
250: expected.put(secKey, values);
251: }
252: values.add(priKey);
253: }
254:
255: open();
256: addEntities(primary);
257: checkSecondary(manyToOne, manyToOneRaw, expected);
258: checkDelete(manyToOne, manyToOneRaw, expected);
259: close();
260: }
261:
262: /**
263: * { 0:{}, 1:{10}, 2:{20,21}, 3:{30,31,32}, 4:{40,41,42,43}
264: */
265: public void testOneToMany() throws DatabaseException {
266:
267: SortedMap<Integer, SortedSet<Integer>> expected = new TreeMap<Integer, SortedSet<Integer>>();
268:
269: for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
270: for (int i = 0; i < priKey; i += 1) {
271: Integer secKey = (N_RECORDS * priKey) + i;
272: SortedSet<Integer> values = expected.get(secKey);
273: if (values == null) {
274: values = new TreeSet<Integer>();
275: expected.put(secKey, values);
276: }
277: values.add(priKey);
278: }
279: }
280:
281: open();
282: addEntities(primary);
283: checkSecondary(oneToMany, oneToManyRaw, expected);
284: checkDelete(oneToMany, oneToManyRaw, expected);
285: close();
286: }
287:
288: /**
289: * { 0:{}, 1:{0}, 2:{0,1}, 3:{0,1,2}, 4:{0,1,2,3}
290: */
291: public void testManyToMany() throws DatabaseException {
292:
293: SortedMap<Integer, SortedSet<Integer>> expected = new TreeMap<Integer, SortedSet<Integer>>();
294:
295: for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
296: for (int i = 0; i < priKey; i += 1) {
297: Integer secKey = i;
298: SortedSet<Integer> values = expected.get(secKey);
299: if (values == null) {
300: values = new TreeSet<Integer>();
301: expected.put(secKey, values);
302: }
303: values.add(priKey);
304: }
305: }
306:
307: open();
308: addEntities(primary);
309: checkSecondary(manyToMany, manyToManyRaw, expected);
310: checkDelete(manyToMany, manyToManyRaw, expected);
311: close();
312: }
313:
314: private void addEntities(PrimaryIndex<Integer, MyEntity> primary)
315: throws DatabaseException {
316:
317: Transaction txn = txnBegin();
318: for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
319: MyEntity prev = primary.put(txn, new MyEntity(priKey));
320: assertNull(prev);
321: }
322: txnCommit(txn);
323: }
324:
325: private void checkDelete(
326: SecondaryIndex<Integer, Integer, MyEntity> index,
327: SecondaryIndex<Object, Object, RawObject> indexRaw,
328: SortedMap<Integer, SortedSet<Integer>> expected)
329: throws DatabaseException {
330:
331: SortedMap<Integer, SortedSet<Integer>> expectedSubIndex = new TreeMap<Integer, SortedSet<Integer>>();
332:
333: while (expected.size() > 0) {
334: Integer delSecKey = expected.firstKey();
335: SortedSet<Integer> deletedPriKeys = expected
336: .remove(delSecKey);
337: for (SortedSet<Integer> priKeys : expected.values()) {
338: priKeys.removeAll(deletedPriKeys);
339: }
340: Transaction txn = txnBegin();
341: boolean deleted = index.delete(txn, delSecKey);
342: assertEquals(deleted, !deletedPriKeys.isEmpty());
343: deleted = index.delete(txn, delSecKey);
344: assertTrue(!deleted);
345: assertNull(index.get(txn, delSecKey, null));
346: txnCommit(txn);
347: checkSecondary(index, indexRaw, expected);
348: }
349:
350: /*
351: * Delete remaining records so that the primary index is empty. Use
352: * the RawStore for variety.
353: */
354: Transaction txn = txnBegin();
355: for (int priKey = 0; priKey < N_RECORDS; priKey += 1) {
356: primaryRaw.delete(txn, priKey);
357: }
358: txnCommit(txn);
359: checkAllEmpty();
360: }
361:
362: private void checkSecondary(
363: SecondaryIndex<Integer, Integer, MyEntity> index,
364: SecondaryIndex<Object, Object, RawObject> indexRaw,
365: SortedMap<Integer, SortedSet<Integer>> expected)
366: throws DatabaseException {
367:
368: checkIndex(index, expected, keyGetter, entityGetter);
369: checkIndex(index.keysIndex(), expected, keyGetter, keyGetter);
370:
371: checkIndex(indexRaw, expected, rawKeyGetter, rawEntityGetter);
372: checkIndex(indexRaw.keysIndex(), expected, rawKeyGetter,
373: rawKeyGetter);
374:
375: SortedMap<Integer, SortedSet<Integer>> expectedSubIndex = new TreeMap<Integer, SortedSet<Integer>>();
376:
377: for (Integer secKey : expected.keySet()) {
378: expectedSubIndex.clear();
379: for (Integer priKey : expected.get(secKey)) {
380: SortedSet<Integer> values = new TreeSet<Integer>();
381: values.add(priKey);
382: expectedSubIndex.put(priKey, values);
383: }
384: checkIndex(index.subIndex(secKey), expectedSubIndex,
385: keyGetter, entityGetter);
386: checkIndex(indexRaw.subIndex(secKey), expectedSubIndex,
387: rawKeyGetter, rawEntityGetter);
388: }
389: }
390:
391: private <K, V> void checkIndex(EntityIndex<K, V> index,
392: SortedMap<Integer, SortedSet<Integer>> expected,
393: Getter<K> kGetter, Getter<V> vGetter)
394: throws DatabaseException {
395:
396: SortedMap<K, V> map = index.sortedMap();
397:
398: Transaction txn = txnBegin();
399: for (int i : expected.keySet()) {
400: K k = kGetter.fromInt(i);
401: SortedSet<Integer> dups = expected.get(i);
402: if (dups.isEmpty()) {
403:
404: /* EntityIndex */
405: V v = index.get(txn, k, null);
406: assertNull(v);
407: assertTrue(!index.contains(txn, k, null));
408:
409: /* Map/Collection */
410: v = map.get(i);
411: assertNull(v);
412: assertTrue(!map.containsKey(i));
413: } else {
414: int j = dups.first();
415:
416: /* EntityIndex */
417: V v = index.get(txn, k, null);
418: assertNotNull(v);
419: assertEquals(j, vGetter.getKey(v));
420: assertTrue(index.contains(txn, k, null));
421:
422: /* Map/Collection */
423: v = map.get(i);
424: assertNotNull(v);
425: assertEquals(j, vGetter.getKey(v));
426: assertTrue(map.containsKey(i));
427: assertTrue("" + i + ' ' + j + ' ' + v + ' ' + map, map
428: .containsValue(v));
429: assertTrue(map.keySet().contains(i));
430: assertTrue(map.values().contains(v));
431: assertTrue(map.entrySet().contains(
432: new MapEntryParameter(i, v)));
433: }
434: }
435: txnCommit(txn);
436:
437: int keysSize = expandKeySize(expected);
438: int valuesSize = expandValueSize(expected);
439:
440: /* EntityIndex.count */
441: assertEquals("keysSize=" + keysSize, (long) valuesSize, index
442: .count());
443:
444: /* Map/Collection size */
445: assertEquals(valuesSize, map.size());
446: assertEquals(valuesSize, map.values().size());
447: assertEquals(valuesSize, map.entrySet().size());
448: assertEquals(keysSize, map.keySet().size());
449:
450: /* Map/Collection isEmpty */
451: assertEquals(valuesSize == 0, map.isEmpty());
452: assertEquals(valuesSize == 0, map.values().isEmpty());
453: assertEquals(valuesSize == 0, map.entrySet().isEmpty());
454: assertEquals(keysSize == 0, map.keySet().isEmpty());
455:
456: txn = txnBeginCursor();
457:
458: /* Unconstrained cursors. */
459: checkCursor(index.keys(txn, null), map.keySet(), true,
460: expandKeys(expected), kGetter);
461: checkCursor(index.entities(txn, null), map.values(), false,
462: expandValues(expected), vGetter);
463:
464: /* Range cursors. */
465: if (expected.isEmpty()) {
466: checkOpenRanges(txn, 0, index, expected, kGetter, vGetter);
467: checkClosedRanges(txn, 0, 1, index, expected, kGetter,
468: vGetter);
469: } else {
470: int firstKey = expected.firstKey();
471: int lastKey = expected.lastKey();
472: for (int i = firstKey - 1; i <= lastKey + 1; i += 1) {
473: checkOpenRanges(txn, i, index, expected, kGetter,
474: vGetter);
475: int j = i + 1;
476: if (j < lastKey + 1) {
477: checkClosedRanges(txn, i, j, index, expected,
478: kGetter, vGetter);
479: }
480: }
481: }
482:
483: txnCommit(txn);
484: }
485:
486: private <K, V> void checkOpenRanges(Transaction txn, int i,
487: EntityIndex<K, V> index,
488: SortedMap<Integer, SortedSet<Integer>> expected,
489: Getter<K> kGetter, Getter<V> vGetter)
490: throws DatabaseException {
491:
492: SortedMap<K, V> map = index.sortedMap();
493: SortedMap<Integer, SortedSet<Integer>> rangeExpected;
494: K k = kGetter.fromInt(i);
495: K kPlusOne = kGetter.fromInt(i + 1);
496:
497: /* Head range exclusive. */
498: rangeExpected = expected.headMap(i);
499: checkCursor(index.keys(txn, null, false, k, false, null), map
500: .headMap(k).keySet(), true, expandKeys(rangeExpected),
501: kGetter);
502: checkCursor(index.entities(txn, null, false, k, false, null),
503: map.headMap(k).values(), false,
504: expandValues(rangeExpected), vGetter);
505:
506: /* Head range inclusive. */
507: rangeExpected = expected.headMap(i + 1);
508: checkCursor(index.keys(txn, null, false, k, true, null), map
509: .headMap(kPlusOne).keySet(), true,
510: expandKeys(rangeExpected), kGetter);
511: checkCursor(index.entities(txn, null, false, k, true, null),
512: map.headMap(kPlusOne).values(), false,
513: expandValues(rangeExpected), vGetter);
514:
515: /* Tail range exclusive. */
516: rangeExpected = expected.tailMap(i + 1);
517: checkCursor(index.keys(txn, k, false, null, false, null), map
518: .tailMap(kPlusOne).keySet(), true,
519: expandKeys(rangeExpected), kGetter);
520: checkCursor(index.entities(txn, k, false, null, false, null),
521: map.tailMap(kPlusOne).values(), false,
522: expandValues(rangeExpected), vGetter);
523:
524: /* Tail range inclusive. */
525: rangeExpected = expected.tailMap(i);
526: checkCursor(index.keys(txn, k, true, null, false, null), map
527: .tailMap(k).keySet(), true, expandKeys(rangeExpected),
528: kGetter);
529: checkCursor(index.entities(txn, k, true, null, false, null),
530: map.tailMap(k).values(), false,
531: expandValues(rangeExpected), vGetter);
532: }
533:
534: private <K, V> void checkClosedRanges(Transaction txn, int i,
535: int j, EntityIndex<K, V> index,
536: SortedMap<Integer, SortedSet<Integer>> expected,
537: Getter<K> kGetter, Getter<V> vGetter)
538: throws DatabaseException {
539:
540: SortedMap<K, V> map = index.sortedMap();
541: SortedMap<Integer, SortedSet<Integer>> rangeExpected;
542: K k = kGetter.fromInt(i);
543: K kPlusOne = kGetter.fromInt(i + 1);
544: K l = kGetter.fromInt(j);
545: K lPlusOne = kGetter.fromInt(j + 1);
546:
547: /* Sub range exclusive. */
548: rangeExpected = expected.subMap(i + 1, j);
549: checkCursor(index.keys(txn, k, false, l, false, null), map
550: .subMap(kPlusOne, l).keySet(), true,
551: expandKeys(rangeExpected), kGetter);
552: checkCursor(index.entities(txn, k, false, l, false, null), map
553: .subMap(kPlusOne, l).values(), false,
554: expandValues(rangeExpected), vGetter);
555:
556: /* Sub range inclusive. */
557: rangeExpected = expected.subMap(i, j + 1);
558: checkCursor(index.keys(txn, k, true, l, true, null), map
559: .subMap(k, lPlusOne).keySet(), true,
560: expandKeys(rangeExpected), kGetter);
561: checkCursor(index.entities(txn, k, true, l, true, null), map
562: .subMap(k, lPlusOne).values(), false,
563: expandValues(rangeExpected), vGetter);
564: }
565:
566: private List<List<Integer>> expandKeys(
567: SortedMap<Integer, SortedSet<Integer>> map) {
568:
569: List<List<Integer>> list = new ArrayList<List<Integer>>();
570: for (Integer key : map.keySet()) {
571: SortedSet<Integer> values = map.get(key);
572: List<Integer> dups = new ArrayList<Integer>();
573: for (int i = 0; i < values.size(); i += 1) {
574: dups.add(key);
575: }
576: list.add(dups);
577: }
578: return list;
579: }
580:
581: private List<List<Integer>> expandValues(
582: SortedMap<Integer, SortedSet<Integer>> map) {
583:
584: List<List<Integer>> list = new ArrayList<List<Integer>>();
585: for (SortedSet<Integer> values : map.values()) {
586: list.add(new ArrayList<Integer>(values));
587: }
588: return list;
589: }
590:
591: private int expandKeySize(SortedMap<Integer, SortedSet<Integer>> map) {
592:
593: int size = 0;
594: for (SortedSet<Integer> values : map.values()) {
595: if (values.size() > 0) {
596: size += 1;
597: }
598: }
599: return size;
600: }
601:
602: private int expandValueSize(
603: SortedMap<Integer, SortedSet<Integer>> map) {
604:
605: int size = 0;
606: for (SortedSet<Integer> values : map.values()) {
607: size += values.size();
608: }
609: return size;
610: }
611:
612: private <T> void checkCursor(EntityCursor<T> cursor,
613: Collection<T> collection, boolean collectionIsKeySet,
614: List<List<Integer>> expected, Getter<T> getter)
615: throws DatabaseException {
616:
617: boolean first;
618: boolean firstDup;
619: Iterator<T> iterator = collection.iterator();
620:
621: for (List<Integer> dups : expected) {
622: for (int i : dups) {
623: T o = cursor.next();
624: assertNotNull(o);
625: assertEquals(i, getter.getKey(o));
626: /* Value iterator over duplicates. */
627: if (!collectionIsKeySet) {
628: assertTrue(iterator.hasNext());
629: o = iterator.next();
630: assertNotNull(o);
631: assertEquals(i, getter.getKey(o));
632: }
633: }
634: }
635:
636: first = true;
637: for (List<Integer> dups : expected) {
638: firstDup = true;
639: for (int i : dups) {
640: T o = first ? cursor.first() : (firstDup ? cursor
641: .next() : cursor.nextDup());
642: assertNotNull(o);
643: assertEquals(i, getter.getKey(o));
644: first = false;
645: firstDup = false;
646: }
647: }
648:
649: first = true;
650: for (List<Integer> dups : expected) {
651: if (!dups.isEmpty()) {
652: int i = dups.get(0);
653: T o = first ? cursor.first() : cursor.nextNoDup();
654: assertNotNull(o);
655: assertEquals(i, getter.getKey(o));
656: /* Key iterator over non-duplicates. */
657: if (collectionIsKeySet) {
658: assertTrue(iterator.hasNext());
659: o = iterator.next();
660: assertNotNull(o);
661: assertEquals(i, getter.getKey(o));
662: }
663: first = false;
664: }
665: }
666:
667: List<List<Integer>> reversed = new ArrayList<List<Integer>>();
668: for (List<Integer> dups : expected) {
669: ArrayList<Integer> reversedDups = new ArrayList<Integer>(
670: dups);
671: Collections.reverse(reversedDups);
672: reversed.add(reversedDups);
673: }
674: Collections.reverse(reversed);
675:
676: first = true;
677: for (List<Integer> dups : reversed) {
678: for (int i : dups) {
679: T o = first ? cursor.last() : cursor.prev();
680: assertNotNull(o);
681: assertEquals(i, getter.getKey(o));
682: first = false;
683: }
684: }
685:
686: first = true;
687: for (List<Integer> dups : reversed) {
688: firstDup = true;
689: for (int i : dups) {
690: T o = first ? cursor.last() : (firstDup ? cursor.prev()
691: : cursor.prevDup());
692: assertNotNull(o);
693: assertEquals(i, getter.getKey(o));
694: first = false;
695: firstDup = false;
696: }
697: }
698:
699: first = true;
700: for (List<Integer> dups : reversed) {
701: if (!dups.isEmpty()) {
702: int i = dups.get(0);
703: T o = first ? cursor.last() : cursor.prevNoDup();
704: assertNotNull(o);
705: assertEquals(i, getter.getKey(o));
706: first = false;
707: }
708: }
709:
710: cursor.close();
711: }
712:
713: private void checkAllEmpty() throws DatabaseException {
714:
715: checkEmpty(primary);
716: checkEmpty(oneToOne);
717: checkEmpty(oneToMany);
718: checkEmpty(manyToOne);
719: checkEmpty(manyToMany);
720: }
721:
722: private <K, V> void checkEmpty(EntityIndex<K, V> index)
723: throws DatabaseException {
724:
725: EntityCursor<K> keys = index.keys();
726: assertNull(keys.next());
727: assertTrue(!keys.iterator().hasNext());
728: keys.close();
729: EntityCursor<V> entities = index.entities();
730: assertNull(entities.next());
731: assertTrue(!entities.iterator().hasNext());
732: entities.close();
733: }
734:
735: private interface Getter<T> {
736: int getKey(T o);
737:
738: T fromInt(int i);
739: }
740:
741: private static Getter<MyEntity> entityGetter = new Getter<MyEntity>() {
742: public int getKey(MyEntity o) {
743: return o.key;
744: }
745:
746: public MyEntity fromInt(int i) {
747: throw new UnsupportedOperationException();
748: }
749: };
750:
751: private static Getter<Integer> keyGetter = new Getter<Integer>() {
752: public int getKey(Integer o) {
753: return o;
754: }
755:
756: public Integer fromInt(int i) {
757: return Integer.valueOf(i);
758: }
759: };
760:
761: private static Getter<RawObject> rawEntityGetter = new Getter<RawObject>() {
762: public int getKey(RawObject o) {
763: Object val = o.getValues().get("key");
764: return ((Integer) val).intValue();
765: }
766:
767: public RawObject fromInt(int i) {
768: throw new UnsupportedOperationException();
769: }
770: };
771:
772: private static Getter<Object> rawKeyGetter = new Getter<Object>() {
773: public int getKey(Object o) {
774: return ((Integer) o).intValue();
775: }
776:
777: public Object fromInt(int i) {
778: return Integer.valueOf(i);
779: }
780: };
781:
782: @Entity
783: private static class MyEntity {
784:
785: @PrimaryKey
786: private int key;
787:
788: @SecondaryKey(relate=ONE_TO_ONE)
789: private int oneToOne;
790:
791: @SecondaryKey(relate=MANY_TO_ONE)
792: private int manyToOne;
793:
794: @SecondaryKey(relate=ONE_TO_MANY)
795: private Set<Integer> oneToMany = new TreeSet<Integer>();
796:
797: @SecondaryKey(relate=MANY_TO_MANY)
798: private Set<Integer> manyToMany = new TreeSet<Integer>();
799:
800: private MyEntity() {
801: }
802:
803: private MyEntity(int key) {
804:
805: /* example keys: {0, 1, 2, 3, 4} */
806: this .key = key;
807:
808: /* { 0:0, 1:-1, 2:-2, 3:-3, 4:-4 } */
809: oneToOne = -key;
810:
811: /* { 0:0, 1:1, 2:2, 3:0, 4:1 } */
812: manyToOne = key % THREE_TO_ONE;
813:
814: /* { 0:{}, 1:{10}, 2:{20,21}, 3:{30,31,32}, 4:{40,41,42,43} */
815: for (int i = 0; i < key; i += 1) {
816: oneToMany.add((N_RECORDS * key) + i);
817: }
818:
819: /* { 0:{}, 1:{0}, 2:{0,1}, 3:{0,1,2}, 4:{0,1,2,3} */
820: for (int i = 0; i < key; i += 1) {
821: manyToMany.add(i);
822: }
823: }
824:
825: @Override
826: public String toString() {
827: return "MyEntity " + key;
828: }
829: }
830: }
|